Skip to content
Open
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
14 changes: 11 additions & 3 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,11 +1049,19 @@ impl<'a> AstValidator<'a> {
}
}

fn check_item_named(&self, ident: Ident, kind: &str) {
fn check_item_named(&self, ident: Ident, kind: &str, ctxt: AssocCtxt) {
if ident.name != kw::Underscore {
return;
}
self.dcx().emit_err(diagnostics::ItemUnderscore { span: ident.span, kind });

if !self.features.enabled(sym::associated_const_underscore)
&& matches!(ctxt, AssocCtxt::Impl { of_trait: false })
{
let msg = format!("naming associated constants with `_` is unstable");
feature_err(&self.sess, sym::associated_const_underscore, ident.span, msg).emit();
} else if !matches!(ctxt, AssocCtxt::Impl { of_trait: false }) {
self.dcx().emit_err(diagnostics::ItemUnderscore { span: ident.span, kind });
}
}

fn check_nomangle_item_asciionly(&self, ident: Ident, item_span: Span) {
Expand Down Expand Up @@ -2139,7 +2147,7 @@ impl Visitor<'_> for AstValidator<'_> {
}

if let AssocItemKind::Const(ci) = &item.kind {
self.check_item_named(ci.ident, "const");
self.check_item_named(ci.ident, "const", ctxt);
}

let parent_is_const =
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,8 @@ declare_features! (
(unstable, asm_goto_with_outputs, "1.85.0", Some(119364)),
/// Allows the `may_unwind` option in inline assembly.
(unstable, asm_unwind, "1.58.0", Some(93334)),
/// Allows `_` for the name of associated constants.
(unstable, associated_const_underscore, "CURRENT_RUSTC_VERSION", Some(158944)),
/// Allows associated type defaults.
(unstable, associated_type_defaults, "1.2.0", Some(29661)),
/// Allows implementing `AsyncDrop`.
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3414,6 +3414,13 @@ impl<'hir> ImplItem<'hir> {
ImplItemId { owner_id: self.owner_id }
}

/// Returns whether this is an anonymous associated constant in an inherent impl.
pub fn is_anon_const(&self) -> bool {
matches!(self.impl_kind, ImplItemImplKind::Inherent { .. })
&& matches!(self.kind, ImplItemKind::Const(..))
&& self.ident.name == kw::Underscore
}

pub fn vis_span(&self) -> Option<Span> {
match self.impl_kind {
ImplItemImplKind::Trait { .. } => None,
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,7 +615,8 @@ fn suggestion_signature<'tcx>(
);
format!("type {}{generics} = /* Type */{where_clauses};", assoc.name())
}
ty::AssocKind::Const { name, .. } => {
ty::AssocKind::Const { .. } => {
let name = assoc.name();
let ty = tcx.type_of(assoc.def_id).instantiate_identity().skip_norm_wip();
let val = tcx
.infer_ctxt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
std::mem::swap(&mut impl_items1, &mut impl_items2);
}

for &item1 in impl_items1.in_definition_order() {
for &item1 in impl_items1.named_items() {
let collision = impl_items2
.filter_by_name_unhygienic(item1.name())
.any(|&item2| self.compare_hygienically(item1, item2));
Expand All @@ -74,7 +74,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {

let mut seen_items = FxIndexMap::default();
let mut res = Ok(());
for impl_item in impl_items.in_definition_order() {
for &impl_item in impl_items.named_items() {
let span = self.tcx.def_span(impl_item.def_id);
let ident = impl_item.ident(self.tcx);

Expand Down Expand Up @@ -111,7 +111,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
let impl_items2 = self.tcx.associated_items(impl2);

let mut res = Ok(());
for &item1 in impl_items1.in_definition_order() {
for &item1 in impl_items1.named_items() {
let collision = impl_items2
.filter_by_name_unhygienic(item1.name())
.find(|&&item2| self.compare_hygienically(item1, item2));
Expand Down Expand Up @@ -228,7 +228,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
// First obtain a list of existing connected region ids
let mut idents_to_add = SmallVec::<[Symbol; 8]>::new();
let mut ids = impl_items
.in_definition_order()
.named_items()
.filter_map(|item| {
let entry = connected_region_ids.entry(item.name());
if let IndexEntry::Occupied(e) = &entry {
Expand Down
6 changes: 2 additions & 4 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

let wider_candidate_names: Vec<_> = visible_traits
.iter()
.flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).in_definition_order())
.filter_map(|item| {
(!item.is_impl_trait_in_trait() && item.tag() == assoc_tag).then(|| item.name())
})
.flat_map(|trait_def_id| tcx.associated_items(*trait_def_id).named_items())
.filter_map(|item| (item.tag() == assoc_tag).then(|| item.name()))
.collect();

if let Some(suggested_name) =
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let Some(iterator_item_id) = tcx
.associated_items(iterator_trait_id)
.in_definition_order()
.named_items()
.find(|item| item.name() == sym::Item)
.map(|item| item.def_id)
else {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1884,7 +1884,8 @@ impl<'tcx> Pick<'tcx> {
tcx.def_path_str(this.item.def_id),
));
}
(ty::AssocKind::Const { name, .. }, ty::AssocContainer::Trait) => {
(ty::AssocKind::Const { .. }, ty::AssocContainer::Trait) => {
let name = this.item.name();
let def_id = this.item.container_id(tcx);
lint.span_suggestion(
span,
Expand Down Expand Up @@ -2674,7 +2675,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
let max_dist = max(name.as_str().len(), 3) / 3;
self.tcx
.associated_items(def_id)
.in_definition_order()
.named_items()
.filter(|x| {
if !self.is_relevant_kind_for_mode(x.kind) {
return false;
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_metadata/src/rmeta/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,12 @@ impl CrateMetadata {
fn get_associated_item(&self, tcx: TyCtxt<'_>, id: DefIndex) -> ty::AssocItem {
let kind = match self.def_kind(id) {
DefKind::AssocConst { is_type_const } => {
ty::AssocKind::Const { name: self.item_name(id), is_type_const }
let data = if self.root.tables.is_anon_assoc_const.get(self, id) {
ty::AssocConstData::Anonymous
} else {
ty::AssocConstData::Named(self.item_name(id))
};
ty::AssocKind::Const { data, is_type_const }
}
DefKind::AssocFn => ty::AssocKind::Fn {
name: self.item_name(id),
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1775,6 +1775,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {

record!(self.tables.assoc_container[def_id] <- item.container);

if item.is_anon_const() {
self.tables.is_anon_assoc_const.set(def_id.index, true);
}

if let AssocContainer::Trait = item.container
&& item.is_type()
{
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_metadata/src/rmeta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,7 @@ define_tables! {
explicit_implied_const_bounds: Table<DefIndex, LazyArray<(ty::PolyTraitRef<'static>, Span)>>,
inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
is_anon_assoc_const: Table<DefIndex, bool>,
// Reexported names are not associated with individual `DefId`s,
// e.g. a glob import can introduce a lot of names, all with the same `DefId`.
// That's why the encoded list needs to contain `ModChild` structures describing all the names
Expand Down
33 changes: 27 additions & 6 deletions compiler/rustc_middle/src/ty/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ impl AssocItem {
match self.kind {
ty::AssocKind::Type { data: AssocTypeData::Normal(name) } => Some(name),
ty::AssocKind::Type { data: AssocTypeData::Rpitit(_) } => None,
ty::AssocKind::Const { name, .. } => Some(name),
ty::AssocKind::Const { data: AssocConstData::Named(name), .. } => Some(name),
ty::AssocKind::Const { data: AssocConstData::Anonymous, .. } => None,
ty::AssocKind::Fn { name, .. } => Some(name),
}
}

// Gets the identifier name. Aborts if it lacks one, i.e. is an RPITIT
// associated type.
// Gets the identifier name. Aborts if it lacks one, e.g. for an RPITIT
// associated type or an anonymous associated const.
pub fn name(&self) -> Symbol {
self.opt_name().expect("name of non-Rpitit assoc item")
self.opt_name().expect("name of anonymous associated item")
}

pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
Expand Down Expand Up @@ -120,7 +121,11 @@ impl AssocItem {
tcx.fn_sig(self.def_id).instantiate_identity().skip_binder().to_string()
}
ty::AssocKind::Type { .. } => format!("type {};", self.name()),
ty::AssocKind::Const { name, .. } => {
ty::AssocKind::Const { data, .. } => {
let name = match data {
AssocConstData::Named(name) => name,
AssocConstData::Anonymous => rustc_span::symbol::kw::Underscore,
};
format!("const {}: {:?};", name, tcx.type_of(self.def_id).instantiate_identity())
}
}
Expand Down Expand Up @@ -154,6 +159,10 @@ impl AssocItem {
}
}

pub fn is_anon_const(&self) -> bool {
matches!(self.kind, ty::AssocKind::Const { data: AssocConstData::Anonymous, .. })
}

pub fn is_fn(&self) -> bool {
matches!(self.kind, ty::AssocKind::Fn { .. })
}
Expand Down Expand Up @@ -184,9 +193,16 @@ pub enum AssocTypeData {
Rpitit(ty::ImplTraitInTraitData),
}

#[derive(Copy, Clone, PartialEq, Debug, StableHash, Eq, Hash, Encodable, Decodable)]
pub enum AssocConstData {
Named(Symbol),
/// An associated constant named `_`, which *semantically* has no name.
Anonymous,
}

#[derive(Copy, Clone, PartialEq, Debug, StableHash, Eq, Hash, Encodable, Decodable)]
pub enum AssocKind {
Const { name: Symbol, is_type_const: bool },
Const { data: AssocConstData, is_type_const: bool },
Fn { name: Symbol, has_self: bool },
Type { data: AssocTypeData },
}
Expand Down Expand Up @@ -275,6 +291,11 @@ impl AssocItems {
self.items.iter().map(|(_, v)| v)
}

/// Returns named associated items in definition order.
pub fn named_items(&self) -> impl '_ + Iterator<Item = &ty::AssocItem> {
self.items.iter().filter_map(|(name, v)| name.is_some().then(|| v))
}

pub fn len(&self) -> usize {
self.items.len()
}
Expand Down
11 changes: 11 additions & 0 deletions compiler/rustc_passes/src/dead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,17 @@ fn maybe_record_as_seed<'tcx>(
}
}
DefKind::AssocFn | DefKind::AssocConst { .. } | DefKind::AssocTy => {
// As with free constants named `_`, associated constants named `_` are always live.
if let hir::Node::ImplItem(impl_item) = tcx.hir_node_by_def_id(owner_id.def_id)
&& impl_item.is_anon_const()
{
push_into_worklist(WorkItem {
id: owner_id.def_id,
propagated: ComesFromAllowExpect::No,
own: ComesFromAllowExpect::No,
});
}

if allow_dead_code.is_none() {
let parent = tcx.local_parent(owner_id.def_id);
match tcx.def_kind(parent) {
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_public/src/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1712,9 +1712,15 @@ pub enum AssocTypeData {
Rpitit(ImplTraitInTraitData),
}

#[derive(Clone, PartialEq, Debug, Eq, Serialize)]
pub enum AssocConstData {
Named(Symbol),
Anonymous,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub enum AssocKind {
Const { name: Symbol },
Const { data: AssocConstData },
Fn { name: Symbol, has_self: bool },
Type { data: AssocTypeData },
}
Expand Down
9 changes: 7 additions & 2 deletions compiler/rustc_public/src/unstable/convert/stable/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1070,9 +1070,14 @@ impl<'tcx> Stable<'tcx> for ty::AssocKind {
tables: &mut Tables<'cx, BridgeTys>,
cx: &CompilerCtxt<'cx, BridgeTys>,
) -> Self::T {
use crate::ty::{AssocKind, AssocTypeData};
use crate::ty::{AssocConstData, AssocKind, AssocTypeData};
match *self {
ty::AssocKind::Const { name, .. } => AssocKind::Const { name: name.to_string() },
ty::AssocKind::Const { data, .. } => AssocKind::Const {
data: match data {
ty::AssocConstData::Named(name) => AssocConstData::Named(name.to_string()),
ty::AssocConstData::Anonymous => AssocConstData::Anonymous,
},
},
ty::AssocKind::Fn { name, has_self } => {
AssocKind::Fn { name: name.to_string(), has_self }
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ symbols! {
assert_zero_valid,
asserting,
associated_const_equality,
associated_const_underscore,
associated_consts,
associated_type_bounds,
associated_type_defaults,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,8 @@ pub fn dyn_compatibility_violations_for_assoc_item(
let span = || item.ident(tcx).span;

match item.kind {
ty::AssocKind::Const { name, is_type_const } => {
ty::AssocKind::Const { is_type_const, .. } => {
let name = item.name();
// We will permit type associated consts if they are explicitly mentioned in the
// trait object type. We can't check this here, as here we only check if it is
// guaranteed to not be possible.
Expand Down
14 changes: 10 additions & 4 deletions compiler/rustc_ty_utils/src/assoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ fn associated_item_from_trait_item(
let owner_id = trait_item.owner_id;
let name = trait_item.ident.name;
let kind = match trait_item.kind {
hir::TraitItemKind::Const(_, _) => {
ty::AssocKind::Const { name, is_type_const: tcx.is_type_const(owner_id.def_id) }
}
hir::TraitItemKind::Const(_, _) => ty::AssocKind::Const {
data: ty::AssocConstData::Named(name),
is_type_const: tcx.is_type_const(owner_id.def_id),
},
hir::TraitItemKind::Fn { .. } => {
ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
}
Expand All @@ -107,7 +108,12 @@ fn associated_item_from_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>
let name = impl_item.ident.name;
let kind = match impl_item.kind {
hir::ImplItemKind::Const(_, rhs) => {
ty::AssocKind::Const { name, is_type_const: matches!(rhs, ConstItemRhs::TypeConst(_)) }
let data = if impl_item.is_anon_const() {
ty::AssocConstData::Anonymous
} else {
ty::AssocConstData::Named(name)
};
ty::AssocKind::Const { data, is_type_const: matches!(rhs, ConstItemRhs::TypeConst(_)) }
}
hir::ImplItemKind::Fn { .. } => {
ty::AssocKind::Fn { name, has_self: fn_has_self_parameter(tcx, owner_id) }
Expand Down
1 change: 1 addition & 0 deletions src/librustdoc/clean/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,7 @@ pub(crate) fn build_impl(
.items
.iter()
.map(|&item| tcx.hir_impl_item(item))
.filter(|item| !item.is_anon_const())
.filter(|item| {
// Filter out impl items whose corresponding trait item has `doc(hidden)`
// not to document such impl items.
Expand Down
4 changes: 3 additions & 1 deletion src/librustdoc/clean/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3040,7 +3040,9 @@ fn clean_impl<'tcx>(
let items = impl_
.items
.iter()
.map(|&ii| clean_impl_item(tcx.hir_impl_item(ii), cx))
.map(|&ii| tcx.hir_impl_item(ii))
.filter(|item| !item.is_anon_const())
.map(|item| clean_impl_item(item, cx))
.collect::<Vec<_>>();

// If this impl block is a positive implementation of the Deref trait, then we
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/assigning_clones.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
})
&& let resolved_assoc_items = cx.tcx.associated_items(resolved_impl)
// Only suggest if `clone_from`/`clone_into` is explicitly implemented
&& resolved_assoc_items.in_definition_order().any(|assoc|
&& resolved_assoc_items.named_items().any(|assoc|
match which_trait {
CloneTrait::Clone => assoc.name() == sym::clone_from,
CloneTrait::ToOwned => assoc.name() == sym::clone_into,
Expand Down
11 changes: 11 additions & 0 deletions tests/rustdoc-html/associated-const-underscore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(associated_const_underscore)]

pub struct Struct;

impl Struct {
//@ has associated_const_underscore/struct.Struct.html '//*[@id="associatedconstant.NAMED"]' ''
pub const NAMED: () = ();

//@ !has associated_const_underscore/struct.Struct.html '//*[@id="associatedconstant._"]' ''
pub const _: () = ();
}
11 changes: 11 additions & 0 deletions tests/rustdoc-json/associated-const-underscore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![feature(associated_const_underscore)]

pub struct Struct;

impl Struct {
//@ has "$..index[?(@.name=='NAMED')].inner.assoc_const"
pub const NAMED: () = ();

//@ !has "$..index[?(@.name=='_')]"
pub const _: () = ();
}
Loading