main-termination

This commit is contained in:
xonx
2026-01-19 17:39:46 +00:00
parent b711f95f86
commit dbc08cfd57
4 changed files with 63 additions and 35 deletions
+33 -34
View File
@@ -7,22 +7,23 @@
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt, TypingMode};
use rustc_session::config::EntryFnType;
use rustc_span::Span;
use rustc_span::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_span::{ErrorGuaranteed, Span};
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
use rustc_trait_selection::regions::InferCtxtRegionExt;
use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
use super::check_function_signature;
use crate::errors;
pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) {
pub(crate) fn check_for_entry_fn(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
match tcx.entry_fn(()) {
Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
_ => {}
_ => Ok(()),
}
}
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) -> Result<(), ErrorGuaranteed> {
let main_fnsig = tcx.fn_sig(main_def_id).instantiate_identity();
let main_span = tcx.def_span(main_def_id);
@@ -87,20 +88,20 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
}
}
let mut error = false;
let main_diagnostics_def_id = main_fn_diagnostics_def_id(tcx, main_def_id, main_span);
let main_asyncness = tcx.asyncness(main_def_id);
if main_asyncness.is_async() {
let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
tcx.dcx()
.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span });
error = true;
return Err(tcx
.dcx()
.emit_err(errors::MainFunctionAsync { span: main_span, asyncness: asyncness_span }));
}
if let Some(attr_span) = find_attr!(tcx, main_def_id, TrackCaller(span) => *span) {
tcx.dcx().emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span });
error = true;
return Err(tcx
.dcx()
.emit_err(errors::TrackCallerOnMain { span: attr_span, annotated: main_span }));
}
if !tcx.codegen_fn_attrs(main_def_id).target_features.is_empty()
@@ -108,12 +109,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
&& !tcx.sess.target.is_like_wasm
&& !tcx.sess.opts.actually_rustdoc
{
tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span });
error = true;
}
if error {
return;
return Err(tcx.dcx().emit_err(errors::TargetFeatureOnMain { main: main_span }));
}
// Main should have no WC, so empty param env is OK here.
@@ -123,8 +119,9 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
let return_ty = main_fnsig.output();
let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
let Some(return_ty) = return_ty.no_bound_vars() else {
tcx.dcx().emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span });
return;
return Err(tcx
.dcx()
.emit_err(errors::MainFunctionReturnTypeGeneric { span: return_ty_span }));
};
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
let cause = traits::ObligationCause::new(
@@ -137,8 +134,16 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
ocx.register_bound(cause, param_env, norm_return_ty, term_did);
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if !errors.is_empty() {
infcx.err_ctxt().report_fulfillment_errors(errors);
error = true;
return Err(infcx.err_ctxt().report_fulfillment_errors(errors));
}
let region_errors =
infcx.resolve_regions(main_diagnostics_def_id, param_env, ty::List::empty());
if !region_errors.is_empty() {
return Err(infcx
.err_ctxt()
.report_region_errors(main_diagnostics_def_id, &region_errors));
}
// now we can take the return type of the given main function
expected_return_type = norm_return_ty;
@@ -147,10 +152,6 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
expected_return_type = tcx.types.unit;
}
if error {
return;
}
let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
[],
expected_return_type,
@@ -159,7 +160,7 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
ExternAbi::Rust,
));
if check_function_signature(
check_function_signature(
tcx,
ObligationCause::new(
main_span,
@@ -168,26 +169,24 @@ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
),
main_def_id,
expected_sig,
)
.is_err()
{
return;
}
)?;
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);
tcx.dcx().emit_err(errors::MainFunctionGenericParameters {
return Err(tcx.dcx().emit_err(errors::MainFunctionGenericParameters {
span: generics_param_span.unwrap_or(main_span),
label_span: generics_param_span,
});
}));
} 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);
tcx.dcx().emit_err(errors::WhereClauseOnMain {
return Err(tcx.dcx().emit_err(errors::WhereClauseOnMain {
span: generics_where_clauses_span.unwrap_or(main_span),
generics_span: generics_where_clauses_span,
});
}));
}
Ok(())
}
@@ -2350,7 +2350,8 @@ pub(super) fn check_type_wf(tcx: TyCtxt<'_>, (): ()) -> Result<(), ErrorGuarante
}))
.and(items.par_nested_bodies(|item| tcx.ensure_result().check_well_formed(item)))
.and(items.par_opaques(|item| tcx.ensure_result().check_well_formed(item)));
super::entry::check_for_entry_fn(tcx);
super::entry::check_for_entry_fn(tcx)?;
res
}
@@ -0,0 +1,17 @@
// This test checks that the compiler correctly handles lifetime requirements
// on the `Termination` trait for the `main` function return type.
// See https://github.com/rust-lang/rust/issues/148421
use std::process::ExitCode;
use std::process::Termination;
trait IsStatic {}
impl<'a: 'static> IsStatic for &'a () {}
struct Thing;
impl Termination for Thing where for<'a> &'a (): IsStatic {
fn report(self) -> ExitCode { panic!() }
}
fn main() -> Thing { Thing } //~ ERROR implementation of `IsStatic` is not general enough
@@ -0,0 +1,11 @@
error: implementation of `IsStatic` is not general enough
--> $DIR/main-termination-lifetime-issue-148421.rs:17:14
|
LL | fn main() -> Thing { Thing }
| ^^^^^ implementation of `IsStatic` is not general enough
|
= note: `IsStatic` would have to be implemented for the type `&'0 ()`, for any lifetime `'0`...
= note: ...but `IsStatic` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: aborting due to 1 previous error