| //! Validity checking for weak lang items |
| |
| use crate::session::config; |
| use crate::middle::lang_items; |
| |
| use rustc_data_structures::fx::FxHashSet; |
| use rustc_target::spec::PanicStrategy; |
| use syntax::ast; |
| use syntax::symbol::{Symbol, sym}; |
| use syntax_pos::Span; |
| use crate::hir::def_id::DefId; |
| use crate::hir::intravisit::{Visitor, NestedVisitorMap}; |
| use crate::hir::intravisit; |
| use crate::hir; |
| use crate::ty::TyCtxt; |
| |
| macro_rules! weak_lang_items { |
| ($($name:ident, $item:ident, $sym:ident;)*) => ( |
| |
| struct Context<'a, 'tcx> { |
| tcx: TyCtxt<'tcx>, |
| items: &'a mut lang_items::LanguageItems, |
| } |
| |
| /// Checks the crate for usage of weak lang items, returning a vector of all the |
| /// language items required by this crate, but not defined yet. |
| pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>, |
| items: &mut lang_items::LanguageItems) { |
| // These are never called by user code, they're generated by the compiler. |
| // They will never implicitly be added to the `missing` array unless we do |
| // so here. |
| if items.eh_personality().is_none() { |
| items.missing.push(lang_items::EhPersonalityLangItem); |
| } |
| if tcx.sess.target.target.options.custom_unwind_resume & |
| items.eh_unwind_resume().is_none() { |
| items.missing.push(lang_items::EhUnwindResumeLangItem); |
| } |
| |
| { |
| let mut cx = Context { tcx, items }; |
| tcx.hir().krate().visit_all_item_likes(&mut cx.as_deep_visitor()); |
| } |
| verify(tcx, items); |
| } |
| |
| pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> { |
| lang_items::extract(attrs).and_then(|(name, _)| { |
| $(if name == sym::$name { |
| Some(sym::$sym) |
| } else)* { |
| None |
| } |
| }) |
| } |
| |
| /// Returns `true` if the specified `lang_item` doesn't actually need to be |
| /// present for this compilation. |
| /// |
| /// Not all lang items are always required for each compilation, particularly in |
| /// the case of panic=abort. In these situations some lang items are injected by |
| /// crates and don't actually need to be defined in libstd. |
| pub fn whitelisted(tcx: TyCtxt<'_>, lang_item: lang_items::LangItem) -> bool { |
| // If we're not compiling with unwinding, we won't actually need these |
| // symbols. Other panic runtimes ensure that the relevant symbols are |
| // available to link things together, but they're never exercised. |
| if tcx.sess.panic_strategy() != PanicStrategy::Unwind { |
| return lang_item == lang_items::EhPersonalityLangItem || |
| lang_item == lang_items::EhUnwindResumeLangItem |
| } |
| |
| false |
| } |
| |
| fn verify<'tcx>(tcx: TyCtxt<'tcx>, |
| items: &lang_items::LanguageItems) { |
| // We only need to check for the presence of weak lang items if we're |
| // emitting something that's not an rlib. |
| let needs_check = tcx.sess.crate_types.borrow().iter().any(|kind| { |
| match *kind { |
| config::CrateType::Dylib | |
| config::CrateType::ProcMacro | |
| config::CrateType::Cdylib | |
| config::CrateType::Executable | |
| config::CrateType::Staticlib => true, |
| config::CrateType::Rlib => false, |
| } |
| }); |
| if !needs_check { |
| return |
| } |
| |
| let mut missing = FxHashSet::default(); |
| for &cnum in tcx.crates().iter() { |
| for &item in tcx.missing_lang_items(cnum).iter() { |
| missing.insert(item); |
| } |
| } |
| |
| $( |
| if missing.contains(&lang_items::$item) && |
| !whitelisted(tcx, lang_items::$item) && |
| items.$name().is_none() { |
| if lang_items::$item == lang_items::PanicImplLangItem { |
| tcx.sess.err(&format!("`#[panic_handler]` function required, \ |
| but not found")); |
| } else if lang_items::$item == lang_items::OomLangItem { |
| tcx.sess.err(&format!("`#[alloc_error_handler]` function required, \ |
| but not found")); |
| } else { |
| tcx.sess.err(&format!("language item required, but not found: `{}`", |
| stringify!($name))); |
| } |
| } |
| )* |
| } |
| |
| impl<'a, 'tcx> Context<'a, 'tcx> { |
| fn register(&mut self, name: Symbol, span: Span) { |
| $(if name == sym::$name { |
| if self.items.$name().is_none() { |
| self.items.missing.push(lang_items::$item); |
| } |
| } else)* { |
| span_err!(self.tcx.sess, span, E0264, |
| "unknown external lang item: `{}`", |
| name); |
| } |
| } |
| } |
| |
| impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> { |
| fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'v> { |
| NestedVisitorMap::None |
| } |
| |
| fn visit_foreign_item(&mut self, i: &hir::ForeignItem) { |
| if let Some((lang_item, _)) = lang_items::extract(&i.attrs) { |
| self.register(lang_item, i.span); |
| } |
| intravisit::walk_foreign_item(self, i) |
| } |
| } |
| |
| impl<'tcx> TyCtxt<'tcx> { |
| pub fn is_weak_lang_item(&self, item_def_id: DefId) -> bool { |
| let lang_items = self.lang_items(); |
| let did = Some(item_def_id); |
| |
| $(lang_items.$name() == did)||* |
| } |
| } |
| |
| ) } |
| |
| weak_lang_items! { |
| panic_impl, PanicImplLangItem, rust_begin_unwind; |
| eh_personality, EhPersonalityLangItem, rust_eh_personality; |
| eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume; |
| oom, OomLangItem, rust_oom; |
| } |