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.
This commit is contained in:
Aaron Hill 2020-05-24 23:07:55 -04:00
parent d8ed1b03c2
commit a13d4678fe
No known key found for this signature in database
GPG key ID: B4087E510E98B164
7 changed files with 94 additions and 28 deletions

View file

@ -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;

View file

@ -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;

View file

@ -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(..) => {

View file

@ -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] {

View file

@ -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,
});

View file

@ -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() {}

View file

@ -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`.