1
Fork 0

Rollup merge of #136671 - nnethercote:middle-limits, r=Nadrieril

Overhaul `rustc_middle::limits`

In particular, to make `pattern_complexity` work more like other limits, which then enables some other simplifications.

r? ``@Nadrieril``
This commit is contained in:
Matthias Krüger 2025-02-17 06:37:35 +01:00 committed by GitHub
commit 0c051c8196
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
28 changed files with 100 additions and 97 deletions

View file

@ -1149,7 +1149,7 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
), ),
rustc_attr!( rustc_attr!(
TEST, pattern_complexity, CrateLevel, template!(NameValueStr: "N"), TEST, pattern_complexity_limit, CrateLevel, template!(NameValueStr: "N"),
ErrorFollowing, EncodeCrossCrate::No, ErrorFollowing, EncodeCrossCrate::No,
), ),
]; ];

View file

@ -227,7 +227,7 @@ declare_features! (
/// Allows using `#[omit_gdb_pretty_printer_section]`. /// Allows using `#[omit_gdb_pretty_printer_section]`.
(internal, omit_gdb_pretty_printer_section, "1.5.0", None), (internal, omit_gdb_pretty_printer_section, "1.5.0", None),
/// Set the maximum pattern complexity allowed (not limited by default). /// 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. /// Allows using pattern types.
(internal, pattern_types, "1.79.0", Some(123646)), (internal, pattern_types, "1.79.0", Some(123646)),
/// Allows using `#[prelude_import]` on glob `use` items. /// Allows using `#[prelude_import]` on glob `use` items.

View file

@ -33,6 +33,10 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag
interface_input_file_would_be_overwritten = interface_input_file_would_be_overwritten =
the input file "{$path}" would be overwritten by the generated executable 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 = interface_mixed_bin_crate =
cannot mix `bin` crate type with others cannot mix `bin` crate type with others

View file

@ -127,3 +127,13 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
pub feature: &'a str, pub feature: &'a str,
pub enabled: &'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,
}

View file

@ -10,6 +10,7 @@
mod callbacks; mod callbacks;
pub mod errors; pub mod errors;
pub mod interface; pub mod interface;
mod limits;
pub mod passes; pub mod passes;
mod proc_macro_decls; mod proc_macro_decls;
mod queries; mod queries;

View file

@ -1,61 +1,58 @@
//! Registering limits: //! Registering limits:
//! * recursion_limit, //! - recursion_limit: there are various parts of the compiler that must impose arbitrary limits
//! * move_size_limit, and //! on how deeply they recurse to prevent stack overflow.
//! * type_length_limit //! - move_size_limit
//! - type_length_limit
//! - pattern_complexity_limit
//! //!
//! There are various parts of the compiler that must impose arbitrary limits //! Users can override these limits via an attribute on the crate like
//! on how deeply they recurse to prevent stack overflow. Users can override //! `#![recursion_limit="22"]`. This pass just looks for those attributes.
//! this via an attribute on the crate like `#![recursion_limit="22"]`. This pass
//! just peeks and looks for that attribute.
use std::num::IntErrorKind; use std::num::IntErrorKind;
use rustc_ast::attr::AttributeExt; use rustc_ast::attr::AttributeExt;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_session::{Limit, Limits, Session}; use rustc_session::{Limit, Limits, Session};
use rustc_span::{Symbol, sym}; use rustc_span::{Symbol, sym};
use crate::error::LimitInvalid; use crate::errors::LimitInvalid;
use crate::query::Providers;
pub fn provide(providers: &mut Providers) { pub(crate) fn provide(providers: &mut Providers) {
providers.limits = |tcx, ()| Limits { providers.limits = |tcx, ()| Limits {
recursion_limit: get_recursion_limit(tcx.hir().krate_attrs(), tcx.sess), recursion_limit: get_recursion_limit(tcx.hir().krate_attrs(), tcx.sess),
move_size_limit: get_limit( move_size_limit: get_limit(
tcx.hir().krate_attrs(), tcx.hir().krate_attrs(),
tcx.sess, tcx.sess,
sym::move_size_limit, 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( type_length_limit: get_limit(
tcx.hir().krate_attrs(), tcx.hir().krate_attrs(),
tcx.sess, tcx.sess,
sym::type_length_limit, 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 { // This one is separate because it must be read prior to macro expansion.
get_limit(krate_attrs, sess, sym::recursion_limit, 128) 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( fn get_limit(
krate_attrs: &[impl AttributeExt], krate_attrs: &[impl AttributeExt],
sess: &Session, sess: &Session,
name: Symbol, name: Symbol,
default: usize, default: Limit,
) -> 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 { for attr in krate_attrs {
if !attr.has_name(name) { if !attr.has_name(name) {
continue; continue;
@ -63,7 +60,7 @@ pub fn get_limit_size(
if let Some(sym) = attr.value_str() { if let Some(sym) = attr.value_str() {
match sym.as_str().parse() { match sym.as_str().parse() {
Ok(n) => return Some(n), Ok(n) => return Limit::new(n),
Err(e) => { Err(e) => {
let error_str = match e.kind() { let error_str = match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large", IntErrorKind::PosOverflow => "`limit` is too large",
@ -84,5 +81,5 @@ pub fn get_limit_size(
} }
} }
} }
None default
} }

View file

@ -39,7 +39,7 @@ use rustc_trait_selection::traits;
use tracing::{info, instrument}; use tracing::{info, instrument};
use crate::interface::Compiler; 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 { pub fn parse<'a>(sess: &'a Session) -> ast::Crate {
let krate = sess let krate = sess
@ -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()); |tcx, _| tcx.arena.alloc_from_iter(tcx.resolutions(()).stripped_cfg_items.steal());
providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1; providers.resolutions = |tcx, ()| tcx.resolver_for_lowering_raw(()).1;
providers.early_lint_checks = early_lint_checks; providers.early_lint_checks = early_lint_checks;
limits::provide(providers);
proc_macro_decls::provide(providers); proc_macro_decls::provide(providers);
rustc_const_eval::provide(providers); rustc_const_eval::provide(providers);
rustc_middle::hir::provide(providers); rustc_middle::hir::provide(providers);
@ -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 // because that would require expanding this while in the middle of expansion, which needs to
// know the limit before expanding. // know the limit before expanding.
let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs); 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. /// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find.

View file

@ -81,10 +81,6 @@ middle_failed_writing_file =
middle_layout_references_error = middle_layout_references_error =
the type has an unknown layout the type has an unknown layout
middle_limit_invalid =
`limit` must be a non-negative integer
.label = {$error_str}
middle_opaque_hidden_type_mismatch = middle_opaque_hidden_type_mismatch =
concrete type differs from previous defining opaque type use concrete type differs from previous defining opaque type use
.label = expected `{$self_ty}`, got `{$other_ty}` .label = expected `{$self_ty}`, got `{$other_ty}`

View file

@ -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)] #[derive(Diagnostic)]
#[diag(middle_recursion_limit_reached)] #[diag(middle_recursion_limit_reached)]
#[help] #[help]

View file

@ -30,12 +30,7 @@ pub mod lib_features {
} }
} }
} }
pub mod limits;
pub mod privacy; pub mod privacy;
pub mod region; pub mod region;
pub mod resolve_bound_vars; pub mod resolve_bound_vars;
pub mod stability; pub mod stability;
pub fn provide(providers: &mut crate::query::Providers) {
limits::provide(providers);
}

View file

@ -2168,6 +2168,10 @@ impl<'tcx> TyCtxt<'tcx> {
self.limits(()).move_size_limit 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. /// All traits in the crate graph, including those not visible to the user.
pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx { pub fn all_traits(self) -> impl Iterator<Item = DefId> + 'tcx {
iter::once(LOCAL_CRATE) iter::once(LOCAL_CRATE)

View file

@ -2168,7 +2168,6 @@ pub fn provide(providers: &mut Providers) {
util::provide(providers); util::provide(providers);
print::provide(providers); print::provide(providers);
super::util::bug::provide(providers); super::util::bug::provide(providers);
super::middle::provide(providers);
*providers = Providers { *providers = Providers {
trait_impls_of: trait_def::trait_impls_of_provider, trait_impls_of: trait_def::trait_impls_of_provider,
incoherent_impls: trait_def::incoherent_impls_provider, incoherent_impls: trait_def::incoherent_impls_provider,

View file

@ -10,7 +10,6 @@ use rustc_hir::{self as hir, BindingMode, ByRef, HirId};
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::Level; use rustc_lint::Level;
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::middle::limits::get_limit_size;
use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::visit::Visitor;
use rustc_middle::thir::*; use rustc_middle::thir::*;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
@ -25,7 +24,7 @@ use rustc_session::lint::builtin::{
}; };
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::hygiene::DesugaringKind; use rustc_span::hygiene::DesugaringKind;
use rustc_span::{Ident, Span, sym}; use rustc_span::{Ident, Span};
use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::infer::InferCtxtExt;
use tracing::instrument; use tracing::instrument;
@ -404,18 +403,11 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
arms: &[MatchArm<'p, 'tcx>], arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>, scrut_ty: Ty<'tcx>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let pattern_complexity_limit = let report =
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity); rustc_pattern_analysis::rustc::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
let report = rustc_pattern_analysis::rustc::analyze_match( self.error = Err(err);
&cx, err
&arms, })?;
scrut_ty,
pattern_complexity_limit,
)
.map_err(|err| {
self.error = Err(err);
err
})?;
// Warn unreachable subpatterns. // Warn unreachable subpatterns.
for (arm, is_useful) in report.arm_usefulness.iter() { for (arm, is_useful) in report.arm_usefulness.iter() {

View file

@ -1084,12 +1084,16 @@ pub fn analyze_match<'p, 'tcx>(
tycx: &RustcPatCtxt<'p, 'tcx>, tycx: &RustcPatCtxt<'p, 'tcx>,
arms: &[MatchArm<'p, 'tcx>], arms: &[MatchArm<'p, 'tcx>],
scrut_ty: Ty<'tcx>, scrut_ty: Ty<'tcx>,
pattern_complexity_limit: Option<usize>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> { ) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty); let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee); let scrut_validity = PlaceValidity::from_bool(tycx.known_valid_scrutinee);
let report = let report = compute_match_usefulness(
compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?; 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 // 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. // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.

View file

@ -795,20 +795,21 @@ struct UsefulnessCtxt<'a, 'p, Cx: PatCx> {
/// Track information about the usefulness of branch patterns (see definition of "branch /// Track information about the usefulness of branch patterns (see definition of "branch
/// pattern" at [`BranchPatUsefulness`]). /// pattern" at [`BranchPatUsefulness`]).
branch_usefulness: FxHashMap<PatId, BranchPatUsefulness<'p, Cx>>, 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, complexity_level: usize,
} }
impl<'a, 'p, Cx: PatCx> UsefulnessCtxt<'a, 'p, Cx> { impl<'a, 'p, Cx: PatCx> UsefulnessCtxt<'a, 'p, Cx> {
fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> { fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> {
self.complexity_level += complexity_add; self.complexity_level += complexity_add;
if self if self.complexity_level <= self.complexity_limit {
.complexity_limit Ok(())
.is_some_and(|complexity_limit| complexity_limit < self.complexity_level) } else {
{ self.tycx.complexity_exceeded()
return self.tycx.complexity_exceeded();
} }
Ok(())
} }
} }
@ -1834,7 +1835,7 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
arms: &[MatchArm<'p, Cx>], arms: &[MatchArm<'p, Cx>],
scrut_ty: Cx::Ty, scrut_ty: Cx::Ty,
scrut_validity: PlaceValidity, scrut_validity: PlaceValidity,
complexity_limit: Option<usize>, complexity_limit: usize,
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> { ) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
let mut cx = UsefulnessCtxt { let mut cx = UsefulnessCtxt {
tycx, tycx,

View file

@ -124,7 +124,7 @@ pub fn compute_match_usefulness<'p>(
arms: &[MatchArm<'p, Cx>], arms: &[MatchArm<'p, Cx>],
ty: Ty, ty: Ty,
scrut_validity: PlaceValidity, scrut_validity: PlaceValidity,
complexity_limit: Option<usize>, complexity_limit: usize,
) -> Result<UsefulnessReport<'p, Cx>, ()> { ) -> Result<UsefulnessReport<'p, Cx>, ()> {
init_tracing(); init_tracing();
rustc_pattern_analysis::usefulness::compute_match_usefulness( rustc_pattern_analysis::usefulness::compute_match_usefulness(

View file

@ -14,7 +14,7 @@ fn check(patterns: &[DeconstructedPat<Cx>], complexity_limit: usize) -> Result<(
let ty = *patterns[0].ty(); let ty = *patterns[0].ty();
let arms: Vec<_> = let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); 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| ()) .map(|_report| ())
} }

View file

@ -14,7 +14,8 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<WitnessPat<Cx>> {
let arms: Vec<_> = let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report = 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 report.non_exhaustiveness_witnesses
} }

View file

@ -14,7 +14,8 @@ fn check(patterns: Vec<DeconstructedPat<Cx>>) -> Vec<Vec<usize>> {
let arms: Vec<_> = let arms: Vec<_> =
patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect(); patterns.iter().map(|pat| MatchArm { pat, has_guard: false, arm_data: () }).collect();
let report = 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() report.arm_intersections.into_iter().map(|bitset| bitset.iter().collect()).collect()
} }

View file

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

View file

@ -1512,7 +1512,7 @@ symbols! {
path_main_separator, path_main_separator,
path_to_pathbuf, path_to_pathbuf,
pathbuf_as_path, pathbuf_as_path,
pattern_complexity, pattern_complexity_limit,
pattern_parentheses, pattern_parentheses,
pattern_type, pattern_type,
pattern_types, pattern_types,

View file

@ -95,7 +95,7 @@ impl<'db> MatchCheckCtx<'db> {
let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true)); let place_validity = PlaceValidity::from_bool(known_valid_scrutinee.unwrap_or(true));
// Measured to take ~100ms on modern hardware. // Measured to take ~100ms on modern hardware.
let complexity_limit = Some(500000); let complexity_limit = 500000;
compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit) compute_match_usefulness(self, arms, scrut_ty, place_validity, complexity_limit)
} }

View file

@ -9107,8 +9107,8 @@ The tracking issue for this feature is: [#27721]
deny_since: None, deny_since: None,
}, },
Lint { Lint {
label: "pattern_complexity", label: "pattern_complexity_limit",
description: r##"# `pattern_complexity` description: r##"# `pattern_complexity_limit`
This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use. This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.

View file

@ -0,0 +1,6 @@
// check that `pattern_complexity_limit` is feature-gated
#![pattern_complexity_limit = "42"]
//~^ ERROR: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests
fn main() {}

View file

@ -1,8 +1,8 @@
error[E0658]: the `#[pattern_complexity]` attribute is just used for rustc unit tests and will never be stable error[E0658]: the `#[pattern_complexity_limit]` attribute is just used for rustc unit tests and will never be stable
--> $DIR/feature-gate-pattern-complexity.rs:3:1 --> $DIR/feature-gate-pattern-complexity-limit.rs:3:1
| |
LL | #![pattern_complexity = "42"] LL | #![pattern_complexity_limit = "42"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable = help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date

View file

@ -1,6 +0,0 @@
// check that `pattern_complexity` is feature-gated
#![pattern_complexity = "42"]
//~^ ERROR: the `#[pattern_complexity]` attribute is just used for rustc unit tests
fn main() {}

View file

@ -1,5 +1,5 @@
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![pattern_complexity = "10000"] #![pattern_complexity_limit = "10000"]
#[derive(Default)] #[derive(Default)]
struct BaseCommand { struct BaseCommand {

View file

@ -1,5 +1,5 @@
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![pattern_complexity = "61"] #![pattern_complexity_limit = "61"]
//@ check-pass //@ check-pass
struct BaseCommand { struct BaseCommand {