Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Overhaul rustc_middle::limits #136671

Merged
merged 5 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1149,7 +1149,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
),
rustc_attr!(
TEST, pattern_complexity, CrateLevel, template!(NameValueStr: "N"),
TEST, pattern_complexity_limit, CrateLevel, template!(NameValueStr: "N"),
ErrorFollowing, EncodeCrossCrate::No,
),
];
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ declare_features! (
/// Allows using `#[omit_gdb_pretty_printer_section]`.
(internal, omit_gdb_pretty_printer_section, "1.5.0", None),
/// Set the maximum pattern complexity allowed (not limited by default).
(internal, pattern_complexity, "1.78.0", None),
(internal, pattern_complexity_limit, "1.78.0", None),
/// Allows using pattern types.
(internal, pattern_types, "1.79.0", Some(123646)),
/// Allows using `#[prelude_import]` on glob `use` items.
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_interface/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag
interface_input_file_would_be_overwritten =
the input file "{$path}" would be overwritten by the generated executable

interface_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}

interface_mixed_bin_crate =
cannot mix `bin` crate type with others

Expand Down
10 changes: 10 additions & 0 deletions compiler/rustc_interface/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,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,
}
1 change: 1 addition & 0 deletions compiler/rustc_interface/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
mod callbacks;
pub mod errors;
pub mod interface;
mod limits;
pub mod passes;
mod proc_macro_decls;
mod queries;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,66 @@
//! Registering limits:
//! * recursion_limit,
//! * move_size_limit, and
//! * type_length_limit
//! - 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
//!
//! There are various parts of the compiler that must impose arbitrary limits
//! on how deeply they recurse to prevent stack overflow. Users can override
//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
//! just peeks and looks for that attribute.
//! 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::error::LimitInvalid;
use crate::query::Providers;
use crate::errors::LimitInvalid;

pub fn provide(providers: &mut Providers) {
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,
tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0),
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,
2usize.pow(24),
Limit::new(2usize.pow(24)),
),
pattern_complexity_limit: get_limit(
tcx.hir().krate_attrs(),
tcx.sess,
sym::pattern_complexity_limit,
Limit::unlimited(),
),
}
}

pub fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit {
get_limit(krate_attrs, sess, sym::recursion_limit, 128)
// 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: usize,
default: Limit,
) -> Limit {
match get_limit_size(krate_attrs, sess, name) {
Some(size) => Limit::new(size),
None => Limit::new(default),
}
}

pub fn get_limit_size(
krate_attrs: &[impl AttributeExt],
sess: &Session,
name: Symbol,
) -> Option<usize> {
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 Some(n),
Ok(n) => return Limit::new(n),
Err(e) => {
let error_str = match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
Expand All @@ -84,5 +81,5 @@ pub fn get_limit_size(
}
}
}
None
default
}
5 changes: 3 additions & 2 deletions compiler/rustc_interface/src/passes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ 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
Expand Down Expand Up @@ -687,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);
Expand Down Expand Up @@ -1134,7 +1135,7 @@ fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit
// 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);
rustc_middle::middle::limits::get_recursion_limit(krate_attrs, sess)
crate::limits::get_recursion_limit(krate_attrs, sess)
}

/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find.
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_middle/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ middle_failed_writing_file =
middle_layout_references_error =
the type has an unknown layout

middle_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}

middle_opaque_hidden_type_mismatch =
concrete type differs from previous defining opaque type use
.label = expected `{$self_ty}`, got `{$other_ty}`
Expand Down
10 changes: 0 additions & 10 deletions compiler/rustc_middle/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,6 @@ pub enum TypeMismatchReason {
},
}

#[derive(Diagnostic)]
#[diag(middle_limit_invalid)]
pub(crate) struct LimitInvalid<'a> {
#[primary_span]
pub span: Span,
#[label]
pub value_span: Span,
pub error_str: &'a str,
}

#[derive(Diagnostic)]
#[diag(middle_recursion_limit_reached)]
#[help]
Expand Down
5 changes: 0 additions & 5 deletions compiler/rustc_middle/src/middle/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,7 @@ pub mod lib_features {
}
}
}
pub mod limits;
pub mod privacy;
pub mod region;
pub mod resolve_bound_vars;
pub mod stability;

pub fn provide(providers: &mut crate::query::Providers) {
limits::provide(providers);
}
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,10 @@ impl<'tcx> TyCtxt<'tcx> {
self.limits(()).move_size_limit
}

pub fn pattern_complexity_limit(self) -> Limit {
self.limits(()).pattern_complexity_limit
}

/// All traits in the crate graph, including those not visible to the user.
pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
iter::once(LOCAL_CRATE)
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2168,7 +2168,6 @@ pub fn provide(providers: &mut Providers) {
util::provide(providers);
print::provide(providers);
super::util::bug::provide(providers);
super::middle::provide(providers);
*providers = Providers {
trait_impls_of: trait_def::trait_impls_of_provider,
incoherent_impls: trait_def::incoherent_impls_provider,
Expand Down
20 changes: 6 additions & 14 deletions compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::Level;
use rustc_middle::bug;
use rustc_middle::middle::limits::get_limit_size;
use rustc_middle::thir::visit::Visitor;
use rustc_middle::thir::*;
use rustc_middle::ty::print::with_no_trimmed_paths;
Expand All @@ -25,7 +24,7 @@ use rustc_session::lint::builtin::{
};
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::{Ident, Span, sym};
use rustc_span::{Ident, Span};
use rustc_trait_selection::infer::InferCtxtExt;
use tracing::instrument;

Expand Down Expand Up @@ -404,18 +403,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let pattern_complexity_limit =
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
let report = rustc_pattern_analysis::rustc::analyze_match(
&cx,
&arms,
scrut_ty,
pattern_complexity_limit,
)
.map_err(|err| {
self.error = Err(err);
err
})?;
let report =
rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
self.error = Err(err);
err
})?;

// Warn unreachable subpatterns.
for (arm, is_useful) in report.arm_usefulness.iter() {
Expand Down
10 changes: 7 additions & 3 deletions compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1084,12 +1084,16 @@ pub fn analyze_match<'p, 'tcx>(
tycx: &RustcPatCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>,
pattern_complexity_limit: Option<usize>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
let report =
compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?;
let report = compute_match_usefulness(
tycx,
arms,
scrut_ty,
scrut_validity,
tycx.tcx.pattern_complexity_limit().0,
)?;

// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
Expand Down
17 changes: 9 additions & 8 deletions compiler/rustc_pattern_analysis/src/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -795,20 +795,21 @@ struct UsefulnessCtxt<'a, 'p, Cx: PatCx> {
/// Track information about the usefulness of branch patterns (see definition of "branch
/// pattern" at [`BranchPatUsefulness`]).
branch_usefulness: FxHashMap<PatId, BranchPatUsefulness<'p, Cx>>,
complexity_limit: Option<usize>,
// Ideally this field would have type `Limit`, but this crate is used by
// rust-analyzer which cannot have a dependency on `Limit`, because `Limit`
// is from crate `rustc_session` which uses unstable Rust features.
complexity_limit: usize,
complexity_level: usize,
}

impl<'a, 'p, Cx: PatCx> UsefulnessCtxt<'a, 'p, Cx> {
fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> {
self.complexity_level += complexity_add;
if self
.complexity_limit
.is_some_and(|complexity_limit| complexity_limit < self.complexity_level)
{
return self.tycx.complexity_exceeded();
if self.complexity_level <= self.complexity_limit {
Ok(())
} else {
self.tycx.complexity_exceeded()
}
Ok(())
}
}

Expand Down Expand Up @@ -1834,7 +1835,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
arms: &[MatchArm<'p, Cx>],
scrut_ty: Cx::Ty,
scrut_validity: PlaceValidity,
complexity_limit: Option<usize>,
complexity_limit: usize,
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
let mut cx = UsefulnessCtxt {
tycx,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_pattern_analysis/tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ pub fn compute_match_usefulness<'p>(
arms: &[MatchArm<'p, Cx>],
ty: Ty,
scrut_validity: PlaceValidity,
complexity_limit: Option<usize>,
complexity_limit: usize,
) -> Result<UsefulnessReport<'p, Cx>, ()> {
init_tracing();
rustc_pattern_analysis::usefulness::compute_match_usefulness(
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_pattern_analysis/tests/complexity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(
let ty = *patterns[0].ty();
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, Some(complexity_limit))
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, complexity_limit)
.map(|_report| ())
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_pattern_analysis/tests/exhaustiveness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report =
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap();
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
.unwrap();
report.non_exhaustiveness_witnesses
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_pattern_analysis/tests/intersection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> {
let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report =
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, None).unwrap();
compute_match_usefulness(arms.as_slice(), ty, PlaceValidity::ValidOnly, usize::MAX)
.unwrap();
report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect()
}

Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_session/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ impl Limit {
Limit(value)
}

/// Create a new unlimited limit.
pub fn unlimited() -> Self {
Limit(usize::MAX)
}

/// Check that `value` is within the limit. Ensures that the same comparisons are used
/// throughout the compiler, as mismatches can cause ICEs, see #72540.
#[inline]
Expand Down Expand Up @@ -119,6 +124,8 @@ pub struct Limits {
pub move_size_limit: Limit,
/// The maximum length of types during monomorphization.
pub type_length_limit: Limit,
/// The maximum pattern complexity allowed (internal only).
pub pattern_complexity_limit: Limit,
}

pub struct CompilerIO {
Expand Down
Loading
Loading