about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2020-05-24 23:07:55 -0400
committerAaron Hill <aa1ronham@gmail.com>2020-06-24 19:08:11 -0400
commita13d4678feadf03e247ab6aae8279bfab9cdaa6d (patch)
tree638f56f743b932f4748845257841d630b2c044d8
parentd8ed1b03c202d77248eb0d335062f46026fc29c4 (diff)
downloadrust-a13d4678feadf03e247ab6aae8279bfab9cdaa6d.tar.gz
rust-a13d4678feadf03e247ab6aae8279bfab9cdaa6d.zip
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.
-rw-r--r--src/libcore/ops/function.rs1
-rw-r--r--src/librustc_hir/lang_items.rs4
-rw-r--r--src/librustc_passes/check_attr.rs5
-rw-r--r--src/librustc_passes/lang_items.rs49
-rw-r--r--src/librustc_trait_selection/traits/project.rs15
-rw-r--r--src/test/ui/assoc-lang-items.rs21
-rw-r--r--src/test/ui/assoc-lang-items.stderr27
7 files changed, 94 insertions, 28 deletions
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<Args>: FnOnce<Args> {
 #[must_use = "closures are lazy and do nothing unless called"]
 pub trait FnOnce<Args> {
     /// 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 @@ use lazy_static::lazy_static;
 // 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 @@ language_item_table! {
     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_session::parse::feature_err;
 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 @@ impl ItemLikeVisitor<'v> for LanguageItemCollector<'tcx> {
         }
     }
 
-    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 crate::traits::error_reporting::InferCtxtExt;
 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`.