diff options
Diffstat (limited to 'compiler/rustc_interface/src')
| -rw-r--r-- | compiler/rustc_interface/src/errors.rs | 25 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/lib.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/limits.rs | 85 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/passes.rs | 110 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/proc_macro_decls.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_interface/src/util.rs | 6 |
6 files changed, 202 insertions, 27 deletions
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index b62950d6709..ca4e556dcdb 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -5,6 +5,21 @@ use rustc_macros::Diagnostic; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] +#[diag(interface_crate_name_does_not_match)] +pub(crate) struct CrateNameDoesNotMatch { + #[primary_span] + pub(crate) span: Span, + pub(crate) crate_name: Symbol, + pub(crate) attr_crate_name: Symbol, +} + +#[derive(Diagnostic)] +#[diag(interface_crate_name_invalid)] +pub(crate) struct CrateNameInvalid<'a> { + pub(crate) crate_name: &'a str, +} + +#[derive(Diagnostic)] #[diag(interface_ferris_identifier)] pub struct FerrisIdentifier { #[primary_span] @@ -112,3 +127,13 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } + +#[derive(Diagnostic)] +#[diag(interface_limit_invalid)] +pub(crate) struct LimitInvalid<'a> { + #[primary_span] + pub span: Span, + #[label] + pub value_span: Span, + pub error_str: &'a str, +} diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index a2a29612e48..54cd341698f 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -10,6 +10,7 @@ mod callbacks; pub mod errors; pub mod interface; +mod limits; pub mod passes; mod proc_macro_decls; mod queries; diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs new file mode 100644 index 00000000000..3de513797e3 --- /dev/null +++ b/compiler/rustc_interface/src/limits.rs @@ -0,0 +1,85 @@ +//! Registering limits: +//! - recursion_limit: there are various parts of the compiler that must impose arbitrary limits +//! on how deeply they recurse to prevent stack overflow. +//! - move_size_limit +//! - type_length_limit +//! - pattern_complexity_limit +//! +//! Users can override these limits via an attribute on the crate like +//! `#![recursion_limit="22"]`. This pass just looks for those attributes. + +use std::num::IntErrorKind; + +use rustc_ast::attr::AttributeExt; +use rustc_middle::bug; +use rustc_middle::query::Providers; +use rustc_session::{Limit, Limits, Session}; +use rustc_span::{Symbol, sym}; + +use crate::errors::LimitInvalid; + +pub(crate) fn provide(providers: &mut Providers) { + providers.limits = |tcx, ()| Limits { + recursion_limit: get_recursion_limit(tcx.hir().krate_attrs(), tcx.sess), + move_size_limit: get_limit( + tcx.hir().krate_attrs(), + tcx.sess, + sym::move_size_limit, + Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0)), + ), + type_length_limit: get_limit( + tcx.hir().krate_attrs(), + tcx.sess, + sym::type_length_limit, + Limit::new(2usize.pow(24)), + ), + pattern_complexity_limit: get_limit( + tcx.hir().krate_attrs(), + tcx.sess, + sym::pattern_complexity_limit, + Limit::unlimited(), + ), + } +} + +// This one is separate because it must be read prior to macro expansion. +pub(crate) fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit { + get_limit(krate_attrs, sess, sym::recursion_limit, Limit::new(128)) +} + +fn get_limit( + krate_attrs: &[impl AttributeExt], + sess: &Session, + name: Symbol, + default: Limit, +) -> Limit { + for attr in krate_attrs { + if !attr.has_name(name) { + continue; + } + + if let Some(sym) = attr.value_str() { + match sym.as_str().parse() { + Ok(n) => return Limit::new(n), + Err(e) => { + let error_str = match e.kind() { + IntErrorKind::PosOverflow => "`limit` is too large", + IntErrorKind::Empty => "`limit` must be a non-negative integer", + IntErrorKind::InvalidDigit => "not a valid integer", + IntErrorKind::NegOverflow => { + bug!("`limit` should never negatively overflow") + } + IntErrorKind::Zero => bug!("zero is a valid `limit`"), + kind => bug!("unimplemented IntErrorKind variant: {:?}", kind), + }; + sess.dcx().emit_err(LimitInvalid { + span: attr.span(), + value_span: attr.value_span().unwrap(), + error_str, + }); + } + } + } + } + default +} diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index e5adcdb244f..d7d183e17ed 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -28,16 +28,18 @@ use rustc_passes::{abi_test, input_stats, layout_test}; use rustc_resolve::Resolver; use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; -use rustc_session::output::{collect_crate_types, filename_for_input, find_crate_name}; +use rustc_session::output::{collect_crate_types, filename_for_input}; use rustc_session::search_paths::PathKind; use rustc_session::{Limit, Session}; -use rustc_span::{ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Symbol, sym}; +use rustc_span::{ + ErrorGuaranteed, FileName, SourceFileHash, SourceFileHashAlgorithm, Span, Symbol, sym, +}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::traits; use tracing::{info, instrument}; use crate::interface::Compiler; -use crate::{errors, proc_macro_decls, util}; +use crate::{errors, limits, proc_macro_decls, util}; pub fn parse<'a>(sess: &'a Session) -> ast::Crate { let krate = sess @@ -685,6 +687,7 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| { |tcx, _| tcx.arena.alloc_from_iter(tcx.resolutions(()).stripped_cfg_items.steal()); providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1; providers.early_lint_checks = early_lint_checks; + limits::provide(providers); proc_macro_decls::provide(providers); rustc_const_eval::provide(providers); rustc_middle::hir::provide(providers); @@ -725,8 +728,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>( let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs); - // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. - let crate_name = find_crate_name(sess, &pre_configured_attrs); + let crate_name = get_crate_name(sess, &pre_configured_attrs); let crate_types = collect_crate_types(sess, &pre_configured_attrs); let stable_crate_id = StableCrateId::new( crate_name, @@ -735,7 +737,7 @@ pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>( sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess); + let dep_graph = setup_dep_graph(sess, crate_name); let cstore = FreezeLock::new(Box::new(CStore::new(compiler.codegen_backend.metadata_loader())) as _); @@ -1080,23 +1082,85 @@ pub(crate) fn start_codegen<'tcx>( codegen } -fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { - if let Some(attr) = krate_attrs - .iter() - .find(|attr| attr.has_name(sym::recursion_limit) && attr.value_str().is_none()) +/// Compute and validate the crate name. +pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol { + // We validate *all* occurrences of `#![crate_name]`, pick the first find and + // if a crate name was passed on the command line via `--crate-name` we enforce + // that they match. + // We perform the validation step here instead of later to ensure it gets run + // in all code paths that require the crate name very early on, namely before + // macro expansion. + + let attr_crate_name = + validate_and_find_value_str_builtin_attr(sym::crate_name, sess, krate_attrs); + + let validate = |name, span| { + rustc_session::output::validate_crate_name(sess, name, span); + name + }; + + if let Some(crate_name) = &sess.opts.crate_name { + let crate_name = Symbol::intern(crate_name); + if let Some((attr_crate_name, span)) = attr_crate_name + && attr_crate_name != crate_name + { + sess.dcx().emit_err(errors::CrateNameDoesNotMatch { + span, + crate_name, + attr_crate_name, + }); + } + return validate(crate_name, None); + } + + if let Some((crate_name, span)) = attr_crate_name { + return validate(crate_name, Some(span)); + } + + if let Input::File(ref path) = sess.io.input + && let Some(file_stem) = path.file_stem().and_then(|s| s.to_str()) { - // This is here mainly to check for using a macro, such as - // #![recursion_limit = foo!()]. That is not supported since that - // would require expanding this while in the middle of expansion, - // which needs to know the limit before expanding. Otherwise, - // validation would normally be caught in AstValidator (via - // `check_builtin_attribute`), but by the time that runs the macro - // is expanded, and it doesn't give an error. - validate_attr::emit_fatal_malformed_builtin_attribute( - &sess.psess, - attr, - sym::recursion_limit, - ); + if file_stem.starts_with('-') { + sess.dcx().emit_err(errors::CrateNameInvalid { crate_name: file_stem }); + } else { + return validate(Symbol::intern(&file_stem.replace('-', "_")), None); + } + } + + sym::rust_out +} + +fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit { + // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`]) + // because that would require expanding this while in the middle of expansion, which needs to + // know the limit before expanding. + let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs); + crate::limits::get_recursion_limit(krate_attrs, sess) +} + +/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find. +/// +/// This validator is intended for built-in attributes whose value needs to be known very early +/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls +/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens +/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion +/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for +/// their presence. +/// +/// [value-str]: ast::Attribute::value_str +fn validate_and_find_value_str_builtin_attr( + name: Symbol, + sess: &Session, + krate_attrs: &[ast::Attribute], +) -> Option<(Symbol, Span)> { + let mut result = None; + // Validate *all* relevant attributes, not just the first occurrence. + for attr in ast::attr::filter_by_name(krate_attrs, name) { + let Some(value) = attr.value_str() else { + validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name) + }; + // Choose the first occurrence as our result. + result.get_or_insert((value, attr.span)); } - rustc_middle::middle::limits::get_recursion_limit(krate_attrs, sess) + result } diff --git a/compiler/rustc_interface/src/proc_macro_decls.rs b/compiler/rustc_interface/src/proc_macro_decls.rs index 82593dbc2b7..00600abb5f1 100644 --- a/compiler/rustc_interface/src/proc_macro_decls.rs +++ b/compiler/rustc_interface/src/proc_macro_decls.rs @@ -7,7 +7,7 @@ use rustc_span::sym; fn proc_macro_decls_static(tcx: TyCtxt<'_>, (): ()) -> Option<LocalDefId> { let mut decls = None; - for id in tcx.hir().items() { + for id in tcx.hir_free_items() { let attrs = tcx.hir().attrs(id.hir_id()); if attr::contains_name(attrs, sym::rustc_proc_macro_decls) { decls = Some(id.owner_id.def_id); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index e900ec14fca..bc2aae7cd87 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -433,11 +433,11 @@ pub(crate) fn check_attr_crate_type( } } else { // This is here mainly to check for using a macro, such as - // #![crate_type = foo!()]. That is not supported since the + // `#![crate_type = foo!()]`. That is not supported since the // crate type needs to be known very early in compilation long // before expansion. Otherwise, validation would normally be - // caught in AstValidator (via `check_builtin_attribute`), but - // by the time that runs the macro is expanded, and it doesn't + // caught during semantic analysis via `TyCtxt::check_mod_attrs`, + // but by the time that runs the macro is expanded, and it doesn't // give an error. validate_attr::emit_fatal_malformed_builtin_attribute( &sess.psess, |
