From a13d4678feadf03e247ab6aae8279bfab9cdaa6d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 24 May 2020 23:07:55 -0400 Subject: [PATCH] Implement associated lang items Fixes #70718 This commit allows making associated items (e.g. associated functions and types) into lang items via the `#[lang]` attribute. This allows such items to be accessed directly, rather than by iterating over the parent item's associated items. I've added `FnOnce::Output` as a lang item, and updated one old usage to use the new lang item. The remaining uses can be updated separately. --- src/libcore/ops/function.rs | 1 + src/librustc_hir/lang_items.rs | 4 +- src/librustc_passes/check_attr.rs | 5 +- src/librustc_passes/lang_items.rs | 49 ++++++++++++------- .../traits/project.rs | 15 +++--- src/test/ui/assoc-lang-items.rs | 21 ++++++++ src/test/ui/assoc-lang-items.stderr | 27 ++++++++++ 7 files changed, 94 insertions(+), 28 deletions(-) create mode 100644 src/test/ui/assoc-lang-items.rs create mode 100644 src/test/ui/assoc-lang-items.stderr diff --git a/src/libcore/ops/function.rs b/src/libcore/ops/function.rs index 04c7789fa4f..2cdfee87a35 100644 --- a/src/libcore/ops/function.rs +++ b/src/libcore/ops/function.rs @@ -224,6 +224,7 @@ pub trait FnMut: FnOnce { #[must_use = "closures are lazy and do nothing unless called"] pub trait FnOnce { /// The returned type after the call operator is used. + #[cfg_attr(not(bootstrap), lang = "fn_once_output")] #[stable(feature = "fn_once_output", since = "1.12.0")] type Output; diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs index 091ded6d74d..cd6f034f7a5 100644 --- a/src/librustc_hir/lang_items.rs +++ b/src/librustc_hir/lang_items.rs @@ -25,7 +25,7 @@ // So you probably just want to nip down to the end. macro_rules! language_item_table { ( - $( $variant:ident, $name:expr, $method:ident, $target:path; )* + $( $variant:ident, $name:expr, $method:ident, $target:expr; )* ) => { enum_from_u32! { @@ -207,6 +207,8 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> { FnMutTraitLangItem, "fn_mut", fn_mut_trait, Target::Trait; FnOnceTraitLangItem, "fn_once", fn_once_trait, Target::Trait; + FnOnceOutputLangItem, "fn_once_output", fn_once_output, Target::AssocTy; + FutureTraitLangItem, "future_trait", future_trait, Target::Trait; GeneratorStateLangItem, "generator_state", gen_state, Target::Enum; GeneratorTraitLangItem, "generator", gen_trait, Target::Trait; diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs index 80681c14375..408f2e8c19a 100644 --- a/src/librustc_passes/check_attr.rs +++ b/src/librustc_passes/check_attr.rs @@ -21,7 +21,10 @@ use rustc_span::symbol::sym; use rustc_span::Span; -fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { +pub(crate) fn target_from_impl_item<'tcx>( + tcx: TyCtxt<'tcx>, + impl_item: &hir::ImplItem<'_>, +) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, hir::ImplItemKind::Fn(..) => { diff --git a/src/librustc_passes/lang_items.rs b/src/librustc_passes/lang_items.rs index 779fb8039d1..f4167c8644e 100644 --- a/src/librustc_passes/lang_items.rs +++ b/src/librustc_passes/lang_items.rs @@ -7,17 +7,19 @@ //! * Traits that represent operators; e.g., `Add`, `Sub`, `Index`. //! * Functions called by the compiler itself. +use crate::check_attr::target_from_impl_item; use crate::weak_lang_items; use rustc_middle::middle::cstore::ExternCrate; use rustc_middle::ty::TyCtxt; +use rustc_ast::ast::Attribute; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items::{extract, ITEM_REFS}; -use rustc_hir::{LangItem, LanguageItems, Target}; +use rustc_hir::{HirId, LangItem, LanguageItems, Target}; use rustc_middle::ty::query::Providers; @@ -28,12 +30,37 @@ struct LanguageItemCollector<'tcx> { impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> { fn visit_item(&mut self, item: &hir::Item<'_>) { - if let Some((value, span)) = extract(&item.attrs) { - let actual_target = Target::from_item(item); + self.check_for_lang(Target::from_item(item), item.hir_id, item.attrs) + } + + fn visit_trait_item(&mut self, trait_item: &hir::TraitItem<'_>) { + self.check_for_lang( + Target::from_trait_item(trait_item), + trait_item.hir_id, + trait_item.attrs, + ) + } + + fn visit_impl_item(&mut self, impl_item: &hir::ImplItem<'_>) { + self.check_for_lang( + target_from_impl_item(self.tcx, impl_item), + impl_item.hir_id, + impl_item.attrs, + ) + } +} + +impl LanguageItemCollector<'tcx> { + fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> { + LanguageItemCollector { tcx, items: LanguageItems::new() } + } + + fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) { + if let Some((value, span)) = extract(&attrs) { match ITEM_REFS.get(&*value.as_str()).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { - let def_id = self.tcx.hir().local_def_id(item.hir_id); + let def_id = self.tcx.hir().local_def_id(hir_id); self.collect_item(item_index, def_id.to_def_id()); } // Known lang item with attribute on incorrect target. @@ -71,20 +98,6 @@ fn visit_item(&mut self, item: &hir::Item<'_>) { } } - fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem<'_>) { - // At present, lang items are always items, not trait items. - } - - fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem<'_>) { - // At present, lang items are always items, not impl items. - } -} - -impl LanguageItemCollector<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> LanguageItemCollector<'tcx> { - LanguageItemCollector { tcx, items: LanguageItems::new() } - } - fn collect_item(&mut self, item_index: usize, item_def_id: DefId) { // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { diff --git a/src/librustc_trait_selection/traits/project.rs b/src/librustc_trait_selection/traits/project.rs index bc696214cbc..f71b3fcf129 100644 --- a/src/librustc_trait_selection/traits/project.rs +++ b/src/librustc_trait_selection/traits/project.rs @@ -23,13 +23,13 @@ use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorReported; use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::{FnOnceTraitLangItem, GeneratorTraitLangItem}; +use rustc_hir::lang_items::{FnOnceOutputLangItem, FnOnceTraitLangItem, GeneratorTraitLangItem}; use rustc_infer::infer::resolve::OpportunisticRegionResolver; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, WithConstness}; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::sym; use rustc_span::DUMMY_SP; pub use rustc_middle::traits::Reveal; @@ -1399,8 +1399,8 @@ fn confirm_callable_candidate<'cx, 'tcx>( debug!("confirm_callable_candidate({:?},{:?})", obligation, fn_sig); - // the `Output` associated type is declared on `FnOnce` let fn_once_def_id = tcx.require_lang_item(FnOnceTraitLangItem, None); + let fn_once_output_def_id = tcx.require_lang_item(FnOnceOutputLangItem, None); let predicate = super::util::closure_trait_ref_and_return_type( tcx, @@ -1410,11 +1410,10 @@ fn confirm_callable_candidate<'cx, 'tcx>( flag, ) .map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate { - projection_ty: ty::ProjectionTy::from_ref_and_name( - tcx, - trait_ref, - Ident::with_dummy_span(rustc_hir::FN_OUTPUT_NAME), - ), + projection_ty: ty::ProjectionTy { + substs: trait_ref.substs, + item_def_id: fn_once_output_def_id, + }, ty: ret_type, }); diff --git a/src/test/ui/assoc-lang-items.rs b/src/test/ui/assoc-lang-items.rs new file mode 100644 index 00000000000..23453d201a7 --- /dev/null +++ b/src/test/ui/assoc-lang-items.rs @@ -0,0 +1,21 @@ +#![feature(lang_items)] + +trait Foo { + #[lang = "dummy_lang_item_1"] //~ ERROR definition + fn foo() {} + + #[lang = "dummy_lang_item_2"] //~ ERROR definition + fn bar(); + + #[lang = "dummy_lang_item_3"] //~ ERROR definition + type MyType; +} + +struct Bar; + +impl Bar { + #[lang = "dummy_lang_item_4"] //~ ERROR definition + fn test() {} +} + +fn main() {} diff --git a/src/test/ui/assoc-lang-items.stderr b/src/test/ui/assoc-lang-items.stderr new file mode 100644 index 00000000000..040792fb1cd --- /dev/null +++ b/src/test/ui/assoc-lang-items.stderr @@ -0,0 +1,27 @@ +error[E0522]: definition of an unknown language item: `dummy_lang_item_1` + --> $DIR/assoc-lang-items.rs:4:5 + | +LL | #[lang = "dummy_lang_item_1"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_1` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_2` + --> $DIR/assoc-lang-items.rs:7:5 + | +LL | #[lang = "dummy_lang_item_2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_2` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_3` + --> $DIR/assoc-lang-items.rs:10:5 + | +LL | #[lang = "dummy_lang_item_3"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_3` + +error[E0522]: definition of an unknown language item: `dummy_lang_item_4` + --> $DIR/assoc-lang-items.rs:17:5 + | +LL | #[lang = "dummy_lang_item_4"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown language item `dummy_lang_item_4` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0522`.