Auto merge of #84401 - crlf0710:impl_main_by_path, r=petrochenkov

Implement RFC 1260 with feature_name `imported_main`.

This is the second extraction part of #84062 plus additional adjustments.
This (mostly) implements RFC 1260.

However there's still one test case failure in the extern crate case. Maybe `LocalDefId` doesn't work here? I'm not sure.

cc https://github.com/rust-lang/rust/issues/28937
r? `@petrochenkov`
This commit is contained in:
bors 2021-04-30 06:59:37 +00:00
commit bcd696d722
38 changed files with 463 additions and 193 deletions

View File

@ -103,7 +103,7 @@ pub(super) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
});
let (main_def_id, entry_ty) = tcx.entry_fn(LOCAL_CRATE).unwrap();
let instance = Instance::mono(tcx, main_def_id.to_def_id()).polymorphize(tcx);
let instance = Instance::mono(tcx, main_def_id).polymorphize(tcx);
match entry_ty {
EntryFnType::Main => {

View File

@ -13,7 +13,7 @@ pub(crate) fn maybe_create_entry_wrapper(
) {
let (main_def_id, use_start_lang_item) = match tcx.entry_fn(LOCAL_CRATE) {
Some((def_id, entry_ty)) => (
def_id.to_def_id(),
def_id,
match entry_ty {
EntryFnType::Main => true,
EntryFnType::Start => false,

View File

@ -344,7 +344,7 @@ impl DebugInfoMethods<'tcx> for CodegenCx<'ll, 'tcx> {
spflags |= DISPFlags::SPFlagOptimized;
}
if let Some((id, _)) = self.tcx.entry_fn(LOCAL_CRATE) {
if id.to_def_id() == def_id {
if id == def_id {
spflags |= DISPFlags::SPFlagMainSubprogram;
}
}

View File

@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
use rustc_data_structures::sync::{par_iter, ParallelIterator};
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
@ -348,12 +348,29 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
) -> Option<Bx::Function> {
let main_def_id = cx.tcx().entry_fn(LOCAL_CRATE).map(|(def_id, _)| def_id)?;
let instance = Instance::mono(cx.tcx(), main_def_id.to_def_id());
let main_is_local = main_def_id.is_local();
let instance = Instance::mono(cx.tcx(), main_def_id);
if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) {
if main_is_local {
// We want to create the wrapper in the same codegen unit as Rust's main
// function.
return None;
if !cx.codegen_unit().contains_item(&MonoItem::Fn(instance)) {
return None;
}
} else {
// FIXME: Add support for non-local main fn codegen
let span = cx.tcx().main_def.unwrap().span;
let n = 28937;
cx.sess()
.struct_span_err(span, "entry symbol `main` from foreign crate is not yet supported.")
.note(&format!(
"see issue #{} <https://github.com/rust-lang/rust/issues/{}> \
for more information",
n, n,
))
.emit();
cx.sess().abort_if_errors();
bug!();
}
let main_llfn = cx.get_fn_addr(instance);
@ -366,7 +383,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
cx: &'a Bx::CodegenCx,
rust_main: Bx::Value,
rust_main_def_id: LocalDefId,
rust_main_def_id: DefId,
use_start_lang_item: bool,
) -> Bx::Function {
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,

View File

@ -1,8 +1,10 @@
#### Note: this error code is no longer emitted by the compiler.
More than one `main` function was found.
Erroneous code example:
```compile_fail,E0136
```compile_fail
fn main() {
// ...
}

View File

@ -20,7 +20,7 @@ use rustc_attr::{self as attr, is_builtin_attr};
use rustc_data_structures::map_in_place::MapInPlace;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, PResult};
use rustc_errors::{Applicability, FatalError, PResult};
use rustc_feature::Features;
use rustc_parse::parser::{AttemptLocalParseRecovery, ForceCollect, Parser, RecoverComma};
use rustc_parse::validate_attr;
@ -414,6 +414,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
kind.article(), kind.descr()
),
);
// FIXME: this workaround issue #84569
FatalError.raise();
}
};
self.cx.trace_macros_diag();

View File

@ -653,6 +653,9 @@ declare_features! (
/// Allows unsizing coercions in `const fn`.
(active, const_fn_unsize, "1.53.0", Some(64992), None),
/// Allows using imported `main` function
(active, imported_main, "1.53.0", Some(28937), None),
// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------

View File

@ -307,7 +307,7 @@ impl<'tcx> Queries<'tcx> {
_ => return,
};
let attrs = &*tcx.get_attrs(def_id.to_def_id());
let attrs = &*tcx.get_attrs(def_id);
let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
for attr in attrs {
match attr.meta_item_list() {

View File

@ -6,7 +6,7 @@ use rustc_data_structures::base_n;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
use rustc_hir::{HirId, ItemId};
use rustc_session::config::OptLevel;
use rustc_span::source_map::Span;
@ -93,7 +93,7 @@ impl<'tcx> MonoItem<'tcx> {
// indicator, then we'll be creating a globally shared version.
if tcx.codegen_fn_attrs(instance.def_id()).contains_extern_indicator()
|| !instance.def.generates_cgu_internal_copy(tcx)
|| Some(instance.def_id()) == entry_def_id.map(LocalDefId::to_def_id)
|| Some(instance.def_id()) == entry_def_id
{
return InstantiationMode::GloballyShared { may_conflict: false };
}

View File

@ -1194,7 +1194,7 @@ rustc_queries! {
/// Identifies the entry-point (e.g., the `main` function) for a given
/// crate, returning `None` if there is no entry point (such as for library crates).
query entry_fn(_: CrateNum) -> Option<(LocalDefId, EntryFnType)> {
query entry_fn(_: CrateNum) -> Option<(DefId, EntryFnType)> {
desc { "looking up the entry function of a crate" }
}
query plugin_registrar_fn(_: CrateNum) -> Option<DefId> {

View File

@ -20,8 +20,8 @@ use crate::ty::TyKind::*;
use crate::ty::{
self, AdtDef, AdtKind, Binder, BindingMode, BoundVar, CanonicalPolyFnSig, Const, ConstVid,
DefIdTree, ExistentialPredicate, FloatTy, FloatVar, FloatVid, GenericParamDefKind, InferConst,
InferTy, IntTy, IntVar, IntVid, List, ParamConst, ParamTy, PolyFnSig, Predicate,
PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions,
InferTy, IntTy, IntVar, IntVid, List, MainDefinition, ParamConst, ParamTy, PolyFnSig,
Predicate, PredicateInner, PredicateKind, ProjectionTy, Region, RegionKind, ReprOptions,
TraitObjectVisitor, Ty, TyKind, TyS, TyVar, TyVid, TypeAndMut, UintTy, Visibility,
};
use rustc_ast as ast;
@ -1025,6 +1025,8 @@ pub struct GlobalCtxt<'tcx> {
layout_interner: ShardedHashMap<&'tcx Layout, ()>,
output_filenames: Arc<OutputFilenames>,
pub main_def: Option<MainDefinition>,
}
impl<'tcx> TyCtxt<'tcx> {
@ -1185,6 +1187,7 @@ impl<'tcx> TyCtxt<'tcx> {
const_stability_interner: Default::default(),
alloc_map: Lock::new(interpret::AllocMap::new()),
output_filenames: Arc::new(output_filenames.clone()),
main_def: resolutions.main_def,
}
}

View File

@ -124,6 +124,20 @@ pub struct ResolverOutputs {
/// Extern prelude entries. The value is `true` if the entry was introduced
/// via `extern crate` item and not `--extern` option or compiler built-in.
pub extern_prelude: FxHashMap<Symbol, bool>,
pub main_def: Option<MainDefinition>,
}
#[derive(Clone, Copy)]
pub struct MainDefinition {
pub res: Res<ast::NodeId>,
pub is_import: bool,
pub span: Span,
}
impl MainDefinition {
pub fn opt_fn_def_id(self) -> Option<DefId> {
if let Res::Def(DefKind::Fn, def_id) = self.res { Some(def_id) } else { None }
}
}
/// The "header" of an impl is everything outside the body: a Self type, a trait

View File

@ -1066,7 +1066,7 @@ struct RootCollector<'a, 'tcx> {
tcx: TyCtxt<'tcx>,
mode: MonoItemCollectionMode,
output: &'a mut Vec<Spanned<MonoItem<'tcx>>>,
entry_fn: Option<(LocalDefId, EntryFnType)>,
entry_fn: Option<(DefId, EntryFnType)>,
}
impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> {
@ -1154,7 +1154,7 @@ impl RootCollector<'_, 'v> {
&& match self.mode {
MonoItemCollectionMode::Eager => true,
MonoItemCollectionMode::Lazy => {
self.entry_fn.map(|(id, _)| id) == Some(def_id)
self.entry_fn.and_then(|(id, _)| id.as_local()) == Some(def_id)
|| self.tcx.is_reachable_non_generic(def_id)
|| self
.tcx

View File

@ -472,7 +472,9 @@ fn create_and_seed_worklist<'tcx>(
)
.chain(
// Seed entry point
tcx.entry_fn(LOCAL_CRATE).map(|(def_id, _)| tcx.hir().local_def_id_to_hir_id(def_id)),
tcx.entry_fn(LOCAL_CRATE).and_then(|(def_id, _)| {
def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id))
}),
)
.collect::<Vec<_>>();

View File

@ -1,12 +1,13 @@
use rustc_ast::entry::EntryPointType;
use rustc_errors::struct_span_err;
use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::{ForeignItem, HirId, ImplItem, Item, ItemKind, TraitItem, CRATE_HIR_ID};
use rustc_middle::hir::map::Map;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{CrateType, EntryFnType};
use rustc_session::parse::feature_err;
use rustc_session::Session;
use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};
@ -16,9 +17,6 @@ struct EntryContext<'a, 'tcx> {
map: Map<'tcx>,
/// The top-level function called `main`.
main_fn: Option<(HirId, Span)>,
/// The function that has attribute named `main`.
attr_main_fn: Option<(HirId, Span)>,
@ -50,7 +48,7 @@ impl<'a, 'tcx> ItemLikeVisitor<'tcx> for EntryContext<'a, 'tcx> {
}
}
fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)> {
fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(DefId, EntryFnType)> {
assert_eq!(cnum, LOCAL_CRATE);
let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable);
@ -67,7 +65,6 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)
let mut ctxt = EntryContext {
session: tcx.sess,
map: tcx.hir(),
main_fn: None,
attr_main_fn: None,
start_fn: None,
non_main_fns: Vec::new(),
@ -115,14 +112,7 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
throw_attr_err(&ctxt.session, attr.span, "rustc_main");
}
}
EntryPointType::MainNamed => {
if ctxt.main_fn.is_none() {
ctxt.main_fn = Some((item.hir_id(), item.span));
} else {
struct_span_err!(ctxt.session, item.span, E0136, "multiple `main` functions")
.emit();
}
}
EntryPointType::MainNamed => (),
EntryPointType::OtherMain => {
ctxt.non_main_fns.push((item.hir_id(), item.span));
}
@ -154,16 +144,23 @@ fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
}
}
fn configure_main(
tcx: TyCtxt<'_>,
visitor: &EntryContext<'_, '_>,
) -> Option<(LocalDefId, EntryFnType)> {
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) -> Option<(DefId, EntryFnType)> {
if let Some((hir_id, _)) = visitor.start_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Start))
Some((tcx.hir().local_def_id(hir_id).to_def_id(), EntryFnType::Start))
} else if let Some((hir_id, _)) = visitor.attr_main_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
} else if let Some((hir_id, _)) = visitor.main_fn {
Some((tcx.hir().local_def_id(hir_id), EntryFnType::Main))
Some((tcx.hir().local_def_id(hir_id).to_def_id(), EntryFnType::Main))
} else if let Some(def_id) = tcx.main_def.and_then(|main_def| main_def.opt_fn_def_id()) {
if tcx.main_def.unwrap().is_import && !tcx.features().imported_main {
let span = tcx.main_def.unwrap().span;
feature_err(
&tcx.sess.parse_sess,
sym::imported_main,
span,
"using an imported function as entry point `main` is experimental",
)
.emit();
}
Some((def_id, EntryFnType::Main))
} else {
no_main_err(tcx, visitor);
None
@ -213,6 +210,14 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
} else {
err.note(&note);
}
if let Some(main_def) = tcx.main_def {
if main_def.opt_fn_def_id().is_none() {
// There is something at `crate::main`, but it is not a function definition.
err.span_label(main_def.span, &format!("non-function item at `crate::main` is found"));
}
}
if tcx.sess.teach(&err.get_code().unwrap()) {
err.note(
"If you don't know the basics of Rust, you can go look to the Rust Book \
@ -222,7 +227,7 @@ fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_, '_>) {
err.emit();
}
pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(LocalDefId, EntryFnType)> {
pub fn find_entry_point(tcx: TyCtxt<'_>) -> Option<(DefId, EntryFnType)> {
tcx.entry_fn(LOCAL_CRATE)
}

View File

@ -50,7 +50,7 @@ use rustc_middle::hir::exports::ExportMap;
use rustc_middle::middle::cstore::{CrateStore, MetadataLoaderDyn};
use rustc_middle::span_bug;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, DefIdTree, ResolverOutputs};
use rustc_middle::ty::{self, DefIdTree, MainDefinition, ResolverOutputs};
use rustc_session::lint;
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
use rustc_session::Session;
@ -1021,6 +1021,8 @@ pub struct Resolver<'a> {
trait_impl_items: FxHashSet<LocalDefId>,
legacy_const_generic_args: FxHashMap<DefId, Option<Vec<usize>>>,
main_def: Option<MainDefinition>,
}
/// Nothing really interesting here; it just provides memory for the rest of the crate.
@ -1348,6 +1350,7 @@ impl<'a> Resolver<'a> {
next_disambiguator: Default::default(),
trait_impl_items: Default::default(),
legacy_const_generic_args: Default::default(),
main_def: Default::default(),
};
let root_parent_scope = ParentScope::module(graph_root, &resolver);
@ -1382,6 +1385,7 @@ impl<'a> Resolver<'a> {
let maybe_unused_trait_imports = self.maybe_unused_trait_imports;
let maybe_unused_extern_crates = self.maybe_unused_extern_crates;
let glob_map = self.glob_map;
let main_def = self.main_def;
ResolverOutputs {
definitions,
cstore: Box::new(self.crate_loader.into_cstore()),
@ -1396,6 +1400,7 @@ impl<'a> Resolver<'a> {
.iter()
.map(|(ident, entry)| (ident.name, entry.introduced_by_item))
.collect(),
main_def,
}
}
@ -1414,6 +1419,7 @@ impl<'a> Resolver<'a> {
.iter()
.map(|(ident, entry)| (ident.name, entry.introduced_by_item))
.collect(),
main_def: self.main_def.clone(),
}
}
@ -1459,6 +1465,7 @@ impl<'a> Resolver<'a> {
self.session.time("finalize_imports", || ImportResolver { r: self }.finalize_imports());
self.session.time("finalize_macro_resolutions", || self.finalize_macro_resolutions());
self.session.time("late_resolve_crate", || self.late_resolve_crate(krate));
self.session.time("resolve_main", || self.resolve_main());
self.session.time("resolve_check_unused", || self.check_unused(krate));
self.session.time("resolve_report_errors", || self.report_errors(krate));
self.session.time("resolve_postprocess", || self.crate_loader.postprocess(krate));
@ -3350,6 +3357,32 @@ impl<'a> Resolver<'a> {
}
None
}
fn resolve_main(&mut self) {
let module = self.graph_root;
let ident = Ident::with_dummy_span(sym::main);
let parent_scope = &ParentScope::module(module, self);
let name_binding = match self.resolve_ident_in_module(
ModuleOrUniformRoot::Module(module),
ident,
ValueNS,
parent_scope,
false,
DUMMY_SP,
) {
Ok(name_binding) => name_binding,
_ => return,
};
let res = name_binding.res();
let is_import = name_binding.is_import();
let span = name_binding.span;
if let Res::Def(DefKind::Fn, _) = res {
self.record_use(ident, ValueNS, name_binding, false);
}
self.main_def = Some(MainDefinition { res, is_import, span });
}
}
fn names_to_string(names: &[Symbol]) -> String {

View File

@ -634,6 +634,7 @@ symbols! {
impl_macros,
impl_trait_in_bindings,
import_shadowing,
imported_main,
in_band_lifetimes,
include,
include_bytes,

View File

@ -6,7 +6,7 @@ use super::*;
use rustc_attr as attr;
use rustc_errors::{Applicability, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{def::Res, ItemKind, Node, PathSegment};
@ -16,15 +16,14 @@ use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::layout::MAX_SIMD_LANES;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::util::{Discr, IntTypeExt};
use rustc_middle::ty::{self, ParamEnv, RegionKind, ToPredicate, Ty, TyCtxt};
use rustc_session::config::EntryFnType;
use rustc_middle::ty::{self, ParamEnv, RegionKind, Ty, TyCtxt};
use rustc_session::lint::builtin::UNINHABITED_STATIC;
use rustc_span::symbol::sym;
use rustc_span::{self, MultiSpan, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{self, ObligationCauseCode};
use rustc_ty_utils::representability::{self, Representability};
use std::iter;
@ -326,29 +325,6 @@ pub(super) fn check_fn<'a, 'tcx>(
}
fcx.demand_suptype(span, revealed_ret_ty, actual_return_ty);
// Check that the main return type implements the termination trait.
if let Some(term_id) = tcx.lang_items().termination() {
if let Some((def_id, EntryFnType::Main)) = tcx.entry_fn(LOCAL_CRATE) {
let main_id = hir.local_def_id_to_hir_id(def_id);
if main_id == fn_id {
let substs = tcx.mk_substs_trait(declared_ret_ty, &[]);
let trait_ref = ty::TraitRef::new(term_id, substs);
let return_ty_span = decl.output.span();
let cause = traits::ObligationCause::new(
return_ty_span,
fn_id,
ObligationCauseCode::MainFunctionType,
);
inherited.register_predicate(traits::Obligation::new(
cause,
param_env,
trait_ref.without_const().to_predicate(tcx),
));
}
}
}
// Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !`
if let Some(panic_impl_did) = tcx.lang_items().panic_impl() {
if panic_impl_did == hir.local_def_id(fn_id).to_def_id() {

View File

@ -116,7 +116,6 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
use rustc_middle::ty::WithConstness;
use rustc_middle::ty::{self, RegionKind, Ty, TyCtxt, UserType};
use rustc_session::config;
use rustc_session::parse::feature_err;

View File

@ -97,8 +97,8 @@ mod variance;
use rustc_errors::{struct_span_err, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::{Node, CRATE_HIR_ID};
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::middle;
@ -110,7 +110,7 @@ use rustc_span::{symbol::sym, Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{
ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
};
use std::iter;
@ -164,106 +164,203 @@ fn require_same_types<'tcx>(
})
}
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) {
let main_id = tcx.hir().local_def_id_to_hir_id(main_def_id);
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
let main_fnsig = tcx.fn_sig(main_def_id);
let main_span = tcx.def_span(main_def_id);
let main_t = tcx.type_of(main_def_id);
match main_t.kind() {
ty::FnDef(..) => {
if let Some(Node::Item(it)) = tcx.hir().find(main_id) {
if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
let mut error = false;
if !generics.params.is_empty() {
let msg = "`main` function is not allowed to have generic \
parameters"
.to_owned();
let label = "`main` cannot have generic parameters".to_string();
struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg)
.span_label(generics.span, label)
.emit();
error = true;
}
if let Some(sp) = generics.where_clause.span() {
struct_span_err!(
tcx.sess,
sp,
E0646,
"`main` function is not allowed to have a `where` clause"
)
.span_label(sp, "`main` cannot have a `where` clause")
.emit();
error = true;
}
if let hir::IsAsync::Async = sig.header.asyncness {
let span = tcx.sess.source_map().guess_head_span(it.span);
struct_span_err!(
tcx.sess,
span,
E0752,
"`main` function is not allowed to be `async`"
)
.span_label(span, "`main` function is not allowed to be `async`")
.emit();
error = true;
}
let attrs = tcx.hir().attrs(main_id);
for attr in attrs {
if tcx.sess.check_name(attr, sym::track_caller) {
tcx.sess
.struct_span_err(
attr.span,
"`main` function is not allowed to be `#[track_caller]`",
)
.span_label(
main_span,
"`main` function is not allowed to be `#[track_caller]`",
)
.emit();
error = true;
}
}
if error {
return;
}
}
fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId {
if let Some(local_def_id) = def_id.as_local() {
let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
let hir_type = tcx.type_of(local_def_id);
if !matches!(hir_type.kind(), ty::FnDef(..)) {
span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
}
let actual = tcx.fn_sig(main_def_id);
let expected_return_type = if tcx.lang_items().termination().is_some() {
// we take the return type of the given main function, the real check is done
// in `check_fn`
actual.output()
} else {
// standard () main return type
ty::Binder::dummy(tcx.mk_unit())
};
let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
tcx.mk_fn_sig(
iter::empty(),
expected_return_type,
false,
hir::Unsafety::Normal,
Abi::Rust,
)
}));
require_same_types(
tcx,
&ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
se_ty,
tcx.mk_fn_ptr(actual),
);
}
_ => {
span_bug!(main_span, "main has a non-function type: found `{}`", main_t);
hir_id
} else {
CRATE_HIR_ID
}
}
}
fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) {
fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
if !def_id.is_local() {
return None;
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
let generics_param_span =
if !generics.params.is_empty() { Some(generics.span) } else { None };
generics_param_span
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
}
}
}
fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
if !def_id.is_local() {
return None;
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
generics.where_clause.span()
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
}
}
}
fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
if !def_id.is_local() {
return None;
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
Some(Node::Item(hir::Item { span: item_span, .. })) => {
Some(tcx.sess.source_map().guess_head_span(*item_span))
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
}
}
}
fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
if !def_id.is_local() {
return None;
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => {
Some(fn_sig.decl.output.span())
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
}
}
}
let mut error = false;
let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span);
let main_fn_generics = tcx.generics_of(main_def_id);
let main_fn_predicates = tcx.predicates_of(main_def_id);
if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
let msg = "`main` function is not allowed to have generic \
parameters";
let mut diag =
struct_span_err!(tcx.sess, generics_param_span.unwrap_or(main_span), E0131, "{}", msg);
if let Some(generics_param_span) = generics_param_span {
let label = "`main` cannot have generic parameters".to_string();
diag.span_label(generics_param_span, label);
}
diag.emit();
error = true;
} else if !main_fn_predicates.predicates.is_empty() {
// generics may bring in implicit predicates, so we skip this check if generics is present.
let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
let mut diag = struct_span_err!(
tcx.sess,
generics_where_clauses_span.unwrap_or(main_span),
E0646,
"`main` function is not allowed to have a `where` clause"
);
if let Some(generics_where_clauses_span) = generics_where_clauses_span {
diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause");
}
diag.emit();
error = true;
}
let main_asyncness = tcx.asyncness(main_def_id);
if let hir::IsAsync::Async = main_asyncness {
let mut diag = struct_span_err!(
tcx.sess,
main_span,
E0752,
"`main` function is not allowed to be `async`"
);
let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
if let Some(asyncness_span) = asyncness_span {
diag.span_label(asyncness_span, "`main` function is not allowed to be `async`");
}
diag.emit();
error = true;
}
for attr in tcx.get_attrs(main_def_id) {
if tcx.sess.check_name(attr, sym::track_caller) {
tcx.sess
.struct_span_err(
attr.span,
"`main` function is not allowed to be `#[track_caller]`",
)
.span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
.emit();
error = true;
}
}
if error {
return;
}
let expected_return_type;
if let Some(term_id) = tcx.lang_items().termination() {
let return_ty = main_fnsig.output();
let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
if !return_ty.bound_vars().is_empty() {
let msg = "`main` function return type is not allowed to have generic \
parameters"
.to_owned();
struct_span_err!(tcx.sess, return_ty_span, E0131, "{}", msg).emit();
error = true;
}
let return_ty = return_ty.skip_binder();
tcx.infer_ctxt().enter(|infcx| {
let cause = traits::ObligationCause::new(
return_ty_span,
main_diagnostics_hir_id,
ObligationCauseCode::MainFunctionType,
);
let mut fulfillment_cx = traits::FulfillmentContext::new();
fulfillment_cx.register_bound(&infcx, ty::ParamEnv::empty(), return_ty, term_id, cause);
if let Err(err) = fulfillment_cx.select_all_or_error(&infcx) {
infcx.report_fulfillment_errors(&err, None, false);
error = true;
}
});
// now we can take the return type of the given main function
expected_return_type = main_fnsig.output();
} else {
// standard () main return type
expected_return_type = ty::Binder::dummy(tcx.mk_unit());
}
if error {
return;
}
let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
tcx.mk_fn_sig(iter::empty(), expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
}));
require_same_types(
tcx,
&ObligationCause::new(
main_span,
main_diagnostics_hir_id,
ObligationCauseCode::MainFunctionType,
),
se_ty,
tcx.mk_fn_ptr(main_fnsig),
);
}
fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
let start_def_id = start_def_id.expect_local();
let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id);
let start_span = tcx.def_span(start_def_id);
let start_t = tcx.type_of(start_def_id);

View File

@ -2,6 +2,5 @@
async fn main() -> Result<i32, ()> {
//~^ ERROR `main` function is not allowed to be `async`
//~^^ ERROR `main` has invalid return type `impl Future`
Ok(1)
}

View File

@ -1,18 +1,9 @@
error[E0277]: `main` has invalid return type `impl Future`
--> $DIR/issue-68523.rs:3:20
|
LL | async fn main() -> Result<i32, ()> {
| ^^^^^^^^^^^^^^^ `main` can only return types that implement `Termination`
|
= help: consider using `()`, or a `Result`
error[E0752]: `main` function is not allowed to be `async`
--> $DIR/issue-68523.rs:3:1
|
LL | async fn main() -> Result<i32, ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` function is not allowed to be `async`
error: aborting due to 2 previous errors
error: aborting due to previous error
Some errors have detailed explanations: E0277, E0752.
For more information about an error, try `rustc --explain E0277`.
For more information about this error, try `rustc --explain E0752`.

View File

@ -0,0 +1 @@
pub fn boilerplate() {}

View File

@ -0,0 +1,7 @@
#![feature(imported_main)]
//~^ ERROR `main` is ambiguous (glob import vs glob import in the same module)
mod m1 { pub(crate) fn main() {} }
mod m2 { pub(crate) fn main() {} }
use m1::*;
use m2::*;

View File

@ -0,0 +1,18 @@
error[E0659]: `main` is ambiguous (glob import vs glob import in the same module)
|
note: `main` could refer to the function imported here
--> $DIR/imported_main_conflict.rs:6:5
|
LL | use m1::*;
| ^^^^^
= help: consider adding an explicit import of `main` to disambiguate
note: `main` could also refer to the function imported here
--> $DIR/imported_main_conflict.rs:7:5
|
LL | use m2::*;
| ^^^^^
= help: consider adding an explicit import of `main` to disambiguate
error: aborting due to previous error
For more information about this error, try `rustc --explain E0659`.

View File

@ -0,0 +1,12 @@
#![feature(imported_main)]
#![feature(min_type_alias_impl_trait, impl_trait_in_bindings)]
#![allow(incomplete_features)]
//~^^^ ERROR `main` function not found in crate
pub mod foo {
type MainFn = impl Fn();
fn bar() {}
pub const BAR: MainFn = bar;
}
use foo::BAR as main;

View File

@ -0,0 +1,17 @@
error[E0601]: `main` function not found in crate `imported_main_const_fn_item_type_forbidden`
--> $DIR/imported_main_const_fn_item_type_forbidden.rs:1:1
|
LL | / #![feature(imported_main)]
LL | | #![feature(min_type_alias_impl_trait, impl_trait_in_bindings)]
LL | | #![allow(incomplete_features)]
LL | |
... |
LL | |
LL | | use foo::BAR as main;
| |_____----------------^ consider adding a `main` function to `$DIR/imported_main_const_fn_item_type_forbidden.rs`
| |
| non-function item at `crate::main` is found
error: aborting due to previous error
For more information about this error, try `rustc --explain E0601`.

View File

@ -0,0 +1,7 @@
#![feature(imported_main)]
//~^ ERROR `main` function not found in crate
pub mod foo {
pub const BAR: usize = 42;
}
use foo::BAR as main;

View File

@ -0,0 +1,17 @@
error[E0601]: `main` function not found in crate `imported_main_const_forbidden`
--> $DIR/imported_main_const_forbidden.rs:1:1
|
LL | / #![feature(imported_main)]
LL | |
LL | | pub mod foo {
LL | | pub const BAR: usize = 42;
LL | | }
LL | |
LL | | use foo::BAR as main;
| |_____----------------^ consider adding a `main` function to `$DIR/imported_main_const_forbidden.rs`
| |
| non-function item at `crate::main` is found
error: aborting due to previous error
For more information about this error, try `rustc --explain E0601`.

View File

@ -0,0 +1,9 @@
// build-fail
// aux-build:main_functions.rs
#![feature(imported_main)]
extern crate main_functions;
pub use main_functions::boilerplate as main; //~ ERROR entry symbol `main` from foreign crate
// FIXME: Should be run-pass

View File

@ -0,0 +1,10 @@
error: entry symbol `main` from foreign crate is not yet supported.
--> $DIR/imported_main_from_extern_crate.rs:7:9
|
LL | pub use main_functions::boilerplate as main;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #28937 <https://github.com/rust-lang/rust/issues/28937> for more information
error: aborting due to previous error

View File

@ -0,0 +1,9 @@
// run-pass
#![feature(imported_main)]
pub mod foo {
pub fn bar() {
println!("Hello world!");
}
}
use foo::bar as main;

View File

@ -0,0 +1,11 @@
// check-pass
#![feature(rustc_attrs)]
#[rustc_main]
fn actual_main() {}
mod foo {
pub(crate) fn something() {}
}
use foo::something as main;

View File

@ -0,0 +1,6 @@
pub mod foo {
pub fn bar() {
println!("Hello world!");
}
}
use foo::bar as main; //~ ERROR using an imported function as entry point

View File

@ -0,0 +1,12 @@
error[E0658]: using an imported function as entry point `main` is experimental
--> $DIR/feature-gate-imported_main.rs:6:5
|
LL | use foo::bar as main;
| ^^^^^^^^^^^^^^^^
|
= note: see issue #28937 <https://github.com/rust-lang/rust/issues/28937> for more information
= help: add `#![feature(imported_main)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -3,6 +3,5 @@
// Test that using a macro to replace the entire crate tree with a non-'mod' item errors out nicely.
// `issue_59191::no_main` replaces whatever's passed in with `fn main() {}`.
#![feature(custom_inner_attributes)]
//~^ ERROR `main` function not found in crate `issue_59191_replace_root_with_fn` [E0601]
#![issue_59191::no_main]
//~^ ERROR expected crate top-level item to be a module after macro expansion, found a function

View File

@ -1,19 +1,10 @@
error: expected crate top-level item to be a module after macro expansion, found a function
--> $DIR/issue-59191-replace-root-with-fn.rs:7:1
--> $DIR/issue-59191-replace-root-with-fn.rs:6:1
|
LL | #![issue_59191::no_main]
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0601]: `main` function not found in crate `issue_59191_replace_root_with_fn`
--> $DIR/issue-59191-replace-root-with-fn.rs:5:1
|
LL | / #![feature(custom_inner_attributes)]
LL | |
LL | | #![issue_59191::no_main]
| |________________________^ consider adding a `main` function to `$DIR/issue-59191-replace-root-with-fn.rs`
error: aborting due to previous error
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0601`.

View File

@ -678,7 +678,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec
pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
cx.tcx
.entry_fn(LOCAL_CRATE)
.map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id.to_def_id())
.map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
}
/// Returns `true` if the expression is in the program's `#[panic_handler]`.