diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs index 2704cb8d78538..c8f1e1dbb0151 100644 --- a/compiler/rustc_attr/src/builtin.rs +++ b/compiler/rustc_attr/src/builtin.rs @@ -454,6 +454,15 @@ pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option { sess.first_attr_value_str_by_name(attrs, sym::crate_name) } +#[derive(Clone, Debug)] +pub struct Condition { + pub name: Symbol, + pub name_span: Span, + pub value: Option, + pub value_span: Option, + pub span: Span, +} + /// Tests if a cfg-pattern matches the cfg set pub fn cfg_matches( cfg: &ast::MetaItem, @@ -462,70 +471,42 @@ pub fn cfg_matches( features: Option<&Features>, ) -> bool { eval_condition(cfg, sess, features, &mut |cfg| { - try_gate_cfg(cfg, sess, features); - let error = |span, msg| { - sess.span_diagnostic.span_err(span, msg); - true - }; - if cfg.path.segments.len() != 1 { - return error(cfg.path.span, "`cfg` predicate key must be an identifier"); - } - match &cfg.kind { - MetaItemKind::List(..) => { - error(cfg.span, "unexpected parentheses after `cfg` predicate key") - } - MetaItemKind::NameValue(lit) if !lit.kind.is_str() => { - handle_errors( - sess, - lit.span, - AttrError::UnsupportedLiteral( - "literal in `cfg` predicate value must be a string", - lit.kind.is_bytestr(), - ), + try_gate_cfg(cfg.name, cfg.span, sess, features); + if let Some(names_valid) = &sess.check_config.names_valid { + if !names_valid.contains(&cfg.name) { + sess.buffer_lint_with_diagnostic( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + "unexpected `cfg` condition name", + BuiltinLintDiagnostics::UnexpectedCfg((cfg.name, cfg.name_span), None), ); - true } - MetaItemKind::NameValue(..) | MetaItemKind::Word => { - let ident = cfg.ident().expect("multi-segment cfg predicate"); - let name = ident.name; - let value = cfg.value_str(); - if let Some(names_valid) = &sess.check_config.names_valid { - if !names_valid.contains(&name) { - sess.buffer_lint_with_diagnostic( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - "unexpected `cfg` condition name", - BuiltinLintDiagnostics::UnexpectedCfg((name, ident.span), None), - ); - } - } - if let Some(value) = value { - if let Some(values) = &sess.check_config.values_valid.get(&name) { - if !values.contains(&value) { - sess.buffer_lint_with_diagnostic( - UNEXPECTED_CFGS, - cfg.span, - lint_node_id, - "unexpected `cfg` condition value", - BuiltinLintDiagnostics::UnexpectedCfg( - (name, ident.span), - Some((value, cfg.name_value_literal_span().unwrap())), - ), - ); - } - } + } + if let Some(value) = cfg.value { + if let Some(values) = &sess.check_config.values_valid.get(&cfg.name) { + if !values.contains(&value) { + sess.buffer_lint_with_diagnostic( + UNEXPECTED_CFGS, + cfg.span, + lint_node_id, + "unexpected `cfg` condition value", + BuiltinLintDiagnostics::UnexpectedCfg( + (cfg.name, cfg.name_span), + cfg.value_span.map(|vs| (value, vs)), + ), + ); } - sess.config.contains(&(name, value)) } } + sess.config.contains(&(cfg.name, cfg.value)) }) } -fn try_gate_cfg(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>) { - let gate = find_gated_cfg(|sym| cfg.has_name(sym)); +fn try_gate_cfg(name: Symbol, span: Span, sess: &ParseSess, features: Option<&Features>) { + let gate = find_gated_cfg(|sym| sym == name); if let (Some(feats), Some(gated_cfg)) = (features, gate) { - gate_cfg(&gated_cfg, cfg.span, sess, feats); + gate_cfg(&gated_cfg, span, sess, feats); } } @@ -563,11 +544,11 @@ pub fn eval_condition( cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Features>, - eval: &mut impl FnMut(&ast::MetaItem) -> bool, + eval: &mut impl FnMut(Condition) -> bool, ) -> bool { match cfg.kind { ast::MetaItemKind::List(ref mis) if cfg.name_or_empty() == sym::version => { - try_gate_cfg(cfg, sess, features); + try_gate_cfg(sym::version, cfg.span, sess, features); let (min_version, span) = match &mis[..] { [NestedMetaItem::Literal(Lit { kind: LitKind::Str(sym, ..), span, .. })] => { (sym, span) @@ -649,6 +630,25 @@ pub fn eval_condition( !eval_condition(mis[0].meta_item().unwrap(), sess, features, eval) } + sym::target => { + if let Some(features) = features && !features.cfg_target_compact { + feature_err( + sess, + sym::cfg_target_compact, + cfg.span, + &"compact `cfg(target(..))` is experimental and subject to change" + ).emit(); + } + + mis.iter().fold(true, |res, mi| { + let mut mi = mi.meta_item().unwrap().clone(); + if let [seg, ..] = &mut mi.path.segments[..] { + seg.ident.name = Symbol::intern(&format!("target_{}", seg.ident.name)); + } + + res & eval_condition(&mi, sess, features, eval) + }) + } _ => { struct_span_err!( sess.span_diagnostic, @@ -662,7 +662,32 @@ pub fn eval_condition( } } } - ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => eval(cfg), + ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => { + sess.span_diagnostic + .span_err(cfg.path.span, "`cfg` predicate key must be an identifier"); + true + } + MetaItemKind::NameValue(ref lit) if !lit.kind.is_str() => { + handle_errors( + sess, + lit.span, + AttrError::UnsupportedLiteral( + "literal in `cfg` predicate value must be a string", + lit.kind.is_bytestr(), + ), + ); + true + } + ast::MetaItemKind::Word | ast::MetaItemKind::NameValue(..) => { + let ident = cfg.ident().expect("multi-segment cfg predicate"); + eval(Condition { + name: ident.name, + name_span: ident.span, + value: cfg.value_str(), + value_span: cfg.name_value_literal_span(), + span: cfg.span, + }) + } } } diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs index c95c1c40a34c2..c3f9f0cf3621f 100644 --- a/compiler/rustc_attr/src/lib.rs +++ b/compiler/rustc_attr/src/lib.rs @@ -4,6 +4,7 @@ //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax` //! to this crate. +#![feature(let_chains)] #![feature(let_else)] #[macro_use] diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 1956327dfaba4..5a02661513ca7 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -319,6 +319,8 @@ declare_features! ( (active, cfg_sanitize, "1.41.0", Some(39699), None), /// Allows `cfg(target_abi = "...")`. (active, cfg_target_abi, "1.55.0", Some(80970), None), + /// Allows `cfg(target(abi = "..."))`. + (active, cfg_target_compact, "1.63.0", Some(96901), None), /// Allows `cfg(target_has_atomic_load_store = "...")`. (active, cfg_target_has_atomic, "1.60.0", Some(94039), None), /// Allows `cfg(target_has_atomic_equal_alignment = "...")`. diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 1e83434b9d062..63103061c9c29 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -536,9 +536,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { fn encode_crate_root(&mut self) -> LazyValue { let tcx = self.tcx; - let mut i = self.position(); + let mut i = 0; + let preamble_bytes = self.position() - i; // Encode the crate deps + i = self.position(); let crate_deps = self.encode_crate_deps(); let dylib_dependency_formats = self.encode_dylib_dependency_formats(); let dep_bytes = self.position() - i; @@ -564,7 +566,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let native_libraries = self.encode_native_libraries(); let native_lib_bytes = self.position() - i; + i = self.position(); let foreign_modules = self.encode_foreign_modules(); + let foreign_modules_bytes = self.position() - i; // Encode DefPathTable i = self.position(); @@ -584,6 +588,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { i = self.position(); let incoherent_impls = self.encode_incoherent_impls(); let incoherent_impls_bytes = self.position() - i; + // Encode MIR. i = self.position(); self.encode_mir(); @@ -596,6 +601,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let item_bytes = self.position() - i; // Encode the allocation index + i = self.position(); let interpret_alloc_index = { let mut interpret_alloc_index = Vec::new(); let mut n = 0; @@ -618,6 +624,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } self.lazy_array(interpret_alloc_index) }; + let interpret_alloc_index_bytes = self.position() - i; // Encode the proc macro data. This affects 'tables', // so we need to do this before we encode the tables @@ -662,9 +669,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { let source_map = self.encode_source_map(); let source_map_bytes = self.position() - i; + i = self.position(); let attrs = tcx.hir().krate_attrs(); let has_default_lib_allocator = tcx.sess.contains_name(&attrs, sym::default_lib_allocator); - let root = self.lazy(CrateRoot { name: tcx.crate_name(LOCAL_CRATE), extra_filename: tcx.sess.opts.cg.extra_filename.clone(), @@ -707,9 +714,34 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { expn_hashes, def_path_hash_map, }); + let final_bytes = self.position() - i; let total_bytes = self.position(); + let computed_total_bytes = preamble_bytes + + dep_bytes + + lib_feature_bytes + + lang_item_bytes + + diagnostic_item_bytes + + native_lib_bytes + + foreign_modules_bytes + + def_path_table_bytes + + traits_bytes + + impls_bytes + + incoherent_impls_bytes + + mir_bytes + + item_bytes + + interpret_alloc_index_bytes + + proc_macro_data_bytes + + tables_bytes + + debugger_visualizers_bytes + + exported_symbols_bytes + + hygiene_bytes + + def_path_hash_map_bytes + + source_map_bytes + + final_bytes; + assert_eq!(total_bytes, computed_total_bytes); + if tcx.sess.meta_stats() { let mut zero_bytes = 0; for e in self.opaque.data.iter() { @@ -718,27 +750,41 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } } - eprintln!("metadata stats:"); - eprintln!(" dep bytes: {}", dep_bytes); - eprintln!(" lib feature bytes: {}", lib_feature_bytes); - eprintln!(" lang item bytes: {}", lang_item_bytes); - eprintln!(" diagnostic item bytes: {}", diagnostic_item_bytes); - eprintln!(" native bytes: {}", native_lib_bytes); - eprintln!(" debugger visualizers bytes: {}", debugger_visualizers_bytes); - eprintln!(" source_map bytes: {}", source_map_bytes); - eprintln!(" traits bytes: {}", traits_bytes); - eprintln!(" impls bytes: {}", impls_bytes); - eprintln!(" incoherent_impls bytes: {}", incoherent_impls_bytes); - eprintln!(" exp. symbols bytes: {}", exported_symbols_bytes); - eprintln!(" def-path table bytes: {}", def_path_table_bytes); - eprintln!(" def-path hashes bytes: {}", def_path_hash_map_bytes); - eprintln!(" proc-macro-data-bytes: {}", proc_macro_data_bytes); - eprintln!(" mir bytes: {}", mir_bytes); - eprintln!(" item bytes: {}", item_bytes); - eprintln!(" table bytes: {}", tables_bytes); - eprintln!(" hygiene bytes: {}", hygiene_bytes); - eprintln!(" zero bytes: {}", zero_bytes); - eprintln!(" total bytes: {}", total_bytes); + let perc = |bytes| (bytes * 100) as f64 / total_bytes as f64; + let p = |label, bytes| { + eprintln!("{:>21}: {:>8} bytes ({:4.1}%)", label, bytes, perc(bytes)); + }; + + eprintln!(""); + eprintln!( + "{} metadata bytes, of which {} bytes ({:.1}%) are zero", + total_bytes, + zero_bytes, + perc(zero_bytes) + ); + p("preamble", preamble_bytes); + p("dep", dep_bytes); + p("lib feature", lib_feature_bytes); + p("lang item", lang_item_bytes); + p("diagnostic item", diagnostic_item_bytes); + p("native lib", native_lib_bytes); + p("foreign modules", foreign_modules_bytes); + p("def-path table", def_path_table_bytes); + p("traits", traits_bytes); + p("impls", impls_bytes); + p("incoherent_impls", incoherent_impls_bytes); + p("mir", mir_bytes); + p("item", item_bytes); + p("interpret_alloc_index", interpret_alloc_index_bytes); + p("proc-macro-data", proc_macro_data_bytes); + p("tables", tables_bytes); + p("debugger visualizers", debugger_visualizers_bytes); + p("exported symbols", exported_symbols_bytes); + p("hygiene", hygiene_bytes); + p("def-path hashes", def_path_hash_map_bytes); + p("source_map", source_map_bytes); + p("final", final_bytes); + eprintln!(""); } root diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs index fb0537c543dc5..0a0c7659b086d 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs @@ -52,11 +52,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } ExprKind::Repeat { value, count } => { - let value_operand = unpack!( - block = - this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No) - ); - block.and(Rvalue::Repeat(value_operand, count)) + if Some(0) == count.try_eval_usize(this.tcx, this.param_env) { + this.build_zero_repeat(block, value, scope, source_info) + } else { + let value_operand = unpack!( + block = this.as_operand( + block, + scope, + &this.thir[value], + None, + NeedsTemporary::No + ) + ); + block.and(Rvalue::Repeat(value_operand, count)) + } } ExprKind::Binary { op, lhs, rhs } => { let lhs = unpack!( @@ -516,6 +525,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + fn build_zero_repeat( + &mut self, + mut block: BasicBlock, + value: ExprId, + scope: Option, + outer_source_info: SourceInfo, + ) -> BlockAnd> { + let this = self; + let value = &this.thir[value]; + let elem_ty = value.ty; + if let Some(Category::Constant) = Category::of(&value.kind) { + // Repeating a const does nothing + } else { + // For a non-const, we may need to generate an appropriate `Drop` + let value_operand = + unpack!(block = this.as_operand(block, scope, value, None, NeedsTemporary::No)); + if let Operand::Move(to_drop) = value_operand { + let success = this.cfg.start_new_block(); + this.cfg.terminate( + block, + outer_source_info, + TerminatorKind::Drop { place: to_drop, target: success, unwind: None }, + ); + this.diverge_from(block); + block = success; + } + this.record_operands_moved(&[value_operand]); + } + block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(elem_ty)), Vec::new())) + } + fn limit_capture_mutability( &mut self, upvar_span: Span, diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 53f9706f021ee..a5f8a5847c270 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -1033,6 +1033,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.block_data(start).terminator().kind, TerminatorKind::Assert { .. } | TerminatorKind::Call { .. } + | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 37e3465694131..bb6d892138a38 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2291,16 +2291,9 @@ impl<'a> Parser<'a> { .span_label(else_span, "expected an `if` or a block after this `else`") .span_suggestion( cond.span.shrink_to_lo(), - "add an `if` if this is the condition to an chained `if` statement after the `else`", + "add an `if` if this is the condition of a chained `else if` statement", "if ".to_string(), Applicability::MaybeIncorrect, - ).multipart_suggestion( - "... otherwise, place this expression inside of a block if it is not an `if` condition", - vec![ - (cond.span.shrink_to_lo(), "{ ".to_string()), - (cond.span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MaybeIncorrect, ) .emit(); self.parse_if_after_cond(AttrVec::new(), cond.span.shrink_to_lo(), cond)? diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 5c9c16350e469..406e9a4113ef3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -427,6 +427,7 @@ symbols! { cfg_panic, cfg_sanitize, cfg_target_abi, + cfg_target_compact, cfg_target_feature, cfg_target_has_atomic, cfg_target_has_atomic_equal_alignment, @@ -1375,6 +1376,7 @@ symbols! { sym, sync, t32, + target, target_abi, target_arch, target_endian, diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs index 7d41819819536..ed7d16f7a5419 100644 --- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs @@ -89,8 +89,8 @@ impl<'tcx> OnUnimplementedDirective { None, ) })?; - attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |item| { - if let Some(symbol) = item.value_str() && let Err(guar) = parse_value(symbol) { + attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| { + if let Some(value) = cfg.value && let Err(guar) = parse_value(value) { errored = Some(guar); } true @@ -226,14 +226,12 @@ impl<'tcx> OnUnimplementedDirective { condition, &tcx.sess.parse_sess, Some(tcx.features()), - &mut |c| { - c.ident().map_or(false, |ident| { - let value = c.value_str().map(|s| { - OnUnimplementedFormatString(s).format(tcx, trait_ref, &options_map) - }); + &mut |cfg| { + let value = cfg.value.map(|v| { + OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map) + }); - options.contains(&(ident.name, value)) - }) + options.contains(&(cfg.name, value)) }, ) { debug!("evaluate: skipping {:?} due to condition", command); diff --git a/library/core/src/asserting.rs b/library/core/src/asserting.rs new file mode 100644 index 0000000000000..212b637d34365 --- /dev/null +++ b/library/core/src/asserting.rs @@ -0,0 +1,109 @@ +// Contains the machinery necessary to print useful `assert!` messages. Not intended for public +// usage, not even nightly use-cases. +// +// Based on https://github.com/dtolnay/case-studies/tree/master/autoref-specialization. When +// 'specialization' is robust enough (5 years? 10 years? Never?), `Capture` can be specialized +// to [Printable]. + +#![allow(missing_debug_implementations)] +#![doc(hidden)] +#![unstable(feature = "generic_assert_internals", issue = "44838")] + +use crate::{ + fmt::{Debug, Formatter}, + marker::PhantomData, +}; + +// ***** TryCapture - Generic ***** + +/// Marker used by [Capture] +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub struct TryCaptureWithoutDebug; + +/// Catches an arbitrary `E` and modifies `to` accordingly +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub trait TryCaptureGeneric { + /// Similar to [TryCapturePrintable] but generic to any `E`. + fn try_capture(&self, to: &mut Capture); +} + +impl TryCaptureGeneric for &Wrapper<&E> { + #[inline] + fn try_capture(&self, _: &mut Capture) {} +} + +impl Debug for Capture { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + f.write_str("N/A") + } +} + +// ***** TryCapture - Printable ***** + +/// Marker used by [Capture] +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub struct TryCaptureWithDebug; + +/// Catches an arbitrary `E: Printable` and modifies `to` accordingly +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub trait TryCapturePrintable { + /// Similar as [TryCaptureGeneric] but specialized to any `E: Printable`. + fn try_capture(&self, to: &mut Capture); +} + +impl TryCapturePrintable for Wrapper<&E> +where + E: Printable, +{ + #[inline] + fn try_capture(&self, to: &mut Capture) { + to.elem = Some(*self.0); + } +} + +impl Debug for Capture +where + E: Printable, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + match self.elem { + None => f.write_str("N/A"), + Some(ref value) => Debug::fmt(value, f), + } + } +} + +// ***** Others ***** + +/// All possible captured `assert!` elements +/// +/// # Types +/// +/// * `E`: **E**lement that is going to be displayed. +/// * `M`: **M**arker used to differentiate [Capture]s in regards to [Debug]. +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub struct Capture { + // If None, then `E` does not implements [Printable] or `E` wasn't evaluated (`assert!( ... )` + // short-circuited). + // + // If Some, then `E` implements [Printable] and was evaluated. + pub elem: Option, + phantom: PhantomData, +} + +impl Capture { + #[inline] + pub const fn new() -> Self { + Self { elem: None, phantom: PhantomData } + } +} + +/// Necessary for the implementations of `TryCapture*` +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub struct Wrapper(pub T); + +/// Tells which elements can be copied and displayed +#[unstable(feature = "generic_assert_internals", issue = "44838")] +pub trait Printable: Copy + Debug {} + +impl Printable for T where T: Copy + Debug {} diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 7b04e4423b782..cfcc3ffb9c092 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -310,6 +310,7 @@ pub mod ops; pub mod any; pub mod array; pub mod ascii; +pub mod asserting; #[unstable(feature = "async_iterator", issue = "79024")] pub mod async_iter; pub mod cell; diff --git a/library/core/tests/asserting.rs b/library/core/tests/asserting.rs new file mode 100644 index 0000000000000..4b626ba6f2d5d --- /dev/null +++ b/library/core/tests/asserting.rs @@ -0,0 +1,37 @@ +use core::asserting::{Capture, TryCaptureGeneric, TryCapturePrintable, Wrapper}; + +macro_rules! test { + ($test_name:ident, $elem:expr, $captured_elem:expr, $output:literal) => { + #[test] + fn $test_name() { + let elem = $elem; + let mut capture = Capture::new(); + assert!(capture.elem == None); + (&Wrapper(&elem)).try_capture(&mut capture); + assert!(capture.elem == $captured_elem); + assert_eq!(format!("{:?}", capture), $output); + } + }; +} + +#[derive(Debug, PartialEq)] +struct NoCopy; + +#[derive(PartialEq)] +struct NoCopyNoDebug; + +#[derive(Clone, Copy, PartialEq)] +struct NoDebug; + +test!( + capture_with_non_copyable_and_non_debugabble_elem_has_correct_params, + NoCopyNoDebug, + None, + "N/A" +); + +test!(capture_with_non_copyable_elem_has_correct_params, NoCopy, None, "N/A"); + +test!(capture_with_non_debugabble_elem_has_correct_params, NoDebug, None, "N/A"); + +test!(capture_with_copyable_and_debugabble_elem_has_correct_params, 1i32, Some(1i32), "1"); diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 004589bbc31cf..9ea374e1045a5 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -35,6 +35,7 @@ #![feature(float_minimum_maximum)] #![feature(future_join)] #![feature(future_poll_fn)] +#![feature(generic_assert_internals)] #![feature(array_try_from_fn)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] @@ -104,6 +105,7 @@ mod alloc; mod any; mod array; mod ascii; +mod asserting; mod atomic; mod bool; mod cell; diff --git a/src/test/ui/cfg/cfg-target-compact-errors.rs b/src/test/ui/cfg/cfg-target-compact-errors.rs new file mode 100644 index 0000000000000..bca2275b1a957 --- /dev/null +++ b/src/test/ui/cfg/cfg-target-compact-errors.rs @@ -0,0 +1,17 @@ +// check-fail + +#![feature(cfg_target_compact)] + +#[cfg(target(o::o))] +//~^ ERROR `cfg` predicate key must be an identifier +fn one() {} + +#[cfg(target(os = 8))] +//~^ ERROR literal in `cfg` predicate value must be a string +fn two() {} + +#[cfg(target(os = "linux", pointer(width = "64")))] +//~^ ERROR invalid predicate `target_pointer` +fn three() {} + +fn main() {} diff --git a/src/test/ui/cfg/cfg-target-compact-errors.stderr b/src/test/ui/cfg/cfg-target-compact-errors.stderr new file mode 100644 index 0000000000000..bb858301eb581 --- /dev/null +++ b/src/test/ui/cfg/cfg-target-compact-errors.stderr @@ -0,0 +1,22 @@ +error: `cfg` predicate key must be an identifier + --> $DIR/cfg-target-compact-errors.rs:5:14 + | +LL | #[cfg(target(o::o))] + | ^^^^ + +error[E0565]: literal in `cfg` predicate value must be a string + --> $DIR/cfg-target-compact-errors.rs:9:19 + | +LL | #[cfg(target(os = 8))] + | ^ + +error[E0537]: invalid predicate `target_pointer` + --> $DIR/cfg-target-compact-errors.rs:13:28 + | +LL | #[cfg(target(os = "linux", pointer(width = "64")))] + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0537, E0565. +For more information about an error, try `rustc --explain E0537`. diff --git a/src/test/ui/cfg/cfg-target-compact.rs b/src/test/ui/cfg/cfg-target-compact.rs new file mode 100644 index 0000000000000..dc95a80915c43 --- /dev/null +++ b/src/test/ui/cfg/cfg-target-compact.rs @@ -0,0 +1,10 @@ +// run-pass +#![feature(cfg_target_compact)] + +#[cfg(target(os = "linux", pointer_width = "64"))] +pub fn main() { +} + +#[cfg(not(target(os = "linux", pointer_width = "64")))] +pub fn main() { +} diff --git a/src/test/ui/check-cfg/compact-names.rs b/src/test/ui/check-cfg/compact-names.rs new file mode 100644 index 0000000000000..bff8074003965 --- /dev/null +++ b/src/test/ui/check-cfg/compact-names.rs @@ -0,0 +1,15 @@ +// This test check that we correctly emit an warning for compact cfg +// +// check-pass +// compile-flags:--check-cfg=names() -Z unstable-options + +#![feature(cfg_target_compact)] + +#[cfg(target(os = "linux", arch = "arm"))] +pub fn expected() {} + +#[cfg(target(os = "linux", architecture = "arm"))] +//~^ WARNING unexpected `cfg` condition name +pub fn unexpected() {} + +fn main() {} diff --git a/src/test/ui/check-cfg/compact-names.stderr b/src/test/ui/check-cfg/compact-names.stderr new file mode 100644 index 0000000000000..f1fc4285a71b7 --- /dev/null +++ b/src/test/ui/check-cfg/compact-names.stderr @@ -0,0 +1,10 @@ +warning: unexpected `cfg` condition name + --> $DIR/compact-names.rs:11:28 + | +LL | #[cfg(target(os = "linux", architecture = "arm"))] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unexpected_cfgs)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/check-cfg/compact-values.rs b/src/test/ui/check-cfg/compact-values.rs new file mode 100644 index 0000000000000..1f17057840cca --- /dev/null +++ b/src/test/ui/check-cfg/compact-values.rs @@ -0,0 +1,15 @@ +// This test check that we correctly emit an warning for compact cfg +// +// check-pass +// compile-flags:--check-cfg=values() -Z unstable-options + +#![feature(cfg_target_compact)] + +#[cfg(target(os = "linux", arch = "arm"))] +pub fn expected() {} + +#[cfg(target(os = "linux", arch = "X"))] +//~^ WARNING unexpected `cfg` condition value +pub fn unexpected() {} + +fn main() {} diff --git a/src/test/ui/check-cfg/compact-values.stderr b/src/test/ui/check-cfg/compact-values.stderr new file mode 100644 index 0000000000000..a196e1537df57 --- /dev/null +++ b/src/test/ui/check-cfg/compact-values.stderr @@ -0,0 +1,11 @@ +warning: unexpected `cfg` condition value + --> $DIR/compact-values.rs:11:28 + | +LL | #[cfg(target(os = "linux", arch = "X"))] + | ^^^^^^^^^^ + | + = note: `#[warn(unexpected_cfgs)]` on by default + = note: expected values for `target_arch` are: aarch64, arm, avr, bpf, hexagon, m68k, mips, mips64, msp430, nvptx64, powerpc, powerpc64, riscv32, riscv64, s390x, sparc, sparc64, wasm32, wasm64, x86, x86_64 + +warning: 1 warning emitted + diff --git a/src/test/ui/drop/repeat-drop-2.rs b/src/test/ui/drop/repeat-drop-2.rs new file mode 100644 index 0000000000000..2e7855328ecbf --- /dev/null +++ b/src/test/ui/drop/repeat-drop-2.rs @@ -0,0 +1,15 @@ +fn borrowck_catch() { + let foo = String::new(); + let _bar = foo; + let _baz = [foo; 0]; //~ ERROR use of moved value: `foo` [E0382] +} + +const _: [String; 0] = [String::new(); 0]; +//~^ ERROR destructors cannot be evaluated at compile-time [E0493] + +fn must_be_init() { + let x: u8; + let _ = [x; 0]; //~ ERROR: use of possibly-uninitialized variable: `x` +} + +fn main() {} diff --git a/src/test/ui/drop/repeat-drop-2.stderr b/src/test/ui/drop/repeat-drop-2.stderr new file mode 100644 index 0000000000000..cdc58180c37b4 --- /dev/null +++ b/src/test/ui/drop/repeat-drop-2.stderr @@ -0,0 +1,29 @@ +error[E0382]: use of moved value: `foo` + --> $DIR/repeat-drop-2.rs:4:17 + | +LL | let foo = String::new(); + | --- move occurs because `foo` has type `String`, which does not implement the `Copy` trait +LL | let _bar = foo; + | --- value moved here +LL | let _baz = [foo; 0]; + | ^^^ value used here after move + +error[E0493]: destructors cannot be evaluated at compile-time + --> $DIR/repeat-drop-2.rs:7:25 + | +LL | const _: [String; 0] = [String::new(); 0]; + | -^^^^^^^^^^^^^---- + | || + | |constants cannot evaluate destructors + | value is dropped here + +error[E0381]: use of possibly-uninitialized variable: `x` + --> $DIR/repeat-drop-2.rs:12:14 + | +LL | let _ = [x; 0]; + | ^ use of possibly-uninitialized `x` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0381, E0382, E0493. +For more information about an error, try `rustc --explain E0381`. diff --git a/src/test/ui/drop/repeat-drop.rs b/src/test/ui/drop/repeat-drop.rs new file mode 100644 index 0000000000000..03e832adb3b3b --- /dev/null +++ b/src/test/ui/drop/repeat-drop.rs @@ -0,0 +1,120 @@ +// run-pass +// ignore-wasm32-bare no unwinding panic +// ignore-avr no unwinding panic +// ignore-nvptx64 no unwinding panic + +static mut CHECK: usize = 0; + +struct DropChecker(usize); + +impl Drop for DropChecker { + fn drop(&mut self) { + unsafe { + if CHECK != self.0 - 1 { + panic!("Found {}, should have found {}", CHECK, self.0 - 1); + } + CHECK = self.0; + } + } +} + +macro_rules! check_drops { + ($l:literal) => { + unsafe { assert_eq!(CHECK, $l) } + }; +} + +struct DropPanic; + +impl Drop for DropPanic { + fn drop(&mut self) { + panic!() + } +} + +fn value_zero() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: [DropChecker; 0] = [foo; 0]; + check_drops!(1); + std::mem::drop(v); + check_drops!(1); +} + +fn value_one() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: [DropChecker; 1] = [foo; 1]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +const DROP_CHECKER: DropChecker = DropChecker(1); + +fn const_zero() { + unsafe { CHECK = 0 }; + let v: [DropChecker; 0] = [DROP_CHECKER; 0]; + check_drops!(0); + std::mem::drop(v); + check_drops!(0); +} + +fn const_one() { + unsafe { CHECK = 0 }; + let v: [DropChecker; 1] = [DROP_CHECKER; 1]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +fn const_generic_zero() { + unsafe { CHECK = 0 }; + let v: [DropChecker; N] = [DROP_CHECKER; N]; + check_drops!(0); + std::mem::drop(v); + check_drops!(0); +} + +fn const_generic_one() { + unsafe { CHECK = 0 }; + let v: [DropChecker; N] = [DROP_CHECKER; N]; + check_drops!(0); + std::mem::drop(v); + check_drops!(1); +} + +// Make sure that things are allowed to promote as expected + +fn allow_promote() { + unsafe { CHECK = 0 }; + let foo = DropChecker(1); + let v: &'static [DropChecker; 0] = &[foo; 0]; + check_drops!(1); + std::mem::drop(v); + check_drops!(1); +} + +// Verify that unwinding in the drop causes the right things to drop in the right order +fn on_unwind() { + unsafe { CHECK = 0 }; + std::panic::catch_unwind(|| { + let panic = DropPanic; + let _local = DropChecker(2); + let _v = (DropChecker(1), [panic; 0]); + std::process::abort(); + }) + .unwrap_err(); + check_drops!(2); +} + +fn main() { + value_zero(); + value_one(); + const_zero(); + const_one(); + const_generic_zero::<0>(); + const_generic_one::<1>(); + allow_promote(); + on_unwind(); +} diff --git a/src/test/ui/feature-gates/feature-gate-cfg-target-compact.rs b/src/test/ui/feature-gates/feature-gate-cfg-target-compact.rs new file mode 100644 index 0000000000000..df81b7d2297ea --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-target-compact.rs @@ -0,0 +1,13 @@ +#[cfg(target(os = "x"))] //~ ERROR compact `cfg(target(..))` is experimental +struct Foo(u64, u64); + +#[cfg_attr(target(os = "x"), x)] //~ ERROR compact `cfg(target(..))` is experimental +struct Bar(u64, u64); + +#[cfg(not(any(all(target(os = "x")))))] //~ ERROR compact `cfg(target(..))` is experimental +fn foo() {} + +fn main() { + cfg!(target(os = "x")); + //~^ ERROR compact `cfg(target(..))` is experimental and subject to change +} diff --git a/src/test/ui/feature-gates/feature-gate-cfg-target-compact.stderr b/src/test/ui/feature-gates/feature-gate-cfg-target-compact.stderr new file mode 100644 index 0000000000000..be6fe23ded171 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-cfg-target-compact.stderr @@ -0,0 +1,39 @@ +error[E0658]: compact `cfg(target(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-compact.rs:1:7 + | +LL | #[cfg(target(os = "x"))] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #96901 for more information + = help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable + +error[E0658]: compact `cfg(target(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-compact.rs:4:12 + | +LL | #[cfg_attr(target(os = "x"), x)] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #96901 for more information + = help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable + +error[E0658]: compact `cfg(target(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-compact.rs:7:19 + | +LL | #[cfg(not(any(all(target(os = "x")))))] + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #96901 for more information + = help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable + +error[E0658]: compact `cfg(target(..))` is experimental and subject to change + --> $DIR/feature-gate-cfg-target-compact.rs:11:10 + | +LL | cfg!(target(os = "x")); + | ^^^^^^^^^^^^^^^^ + | + = note: see issue #96901 for more information + = help: add `#![feature(cfg_target_compact)]` to the crate attributes to enable + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/parser/else-no-if.stderr b/src/test/ui/parser/else-no-if.stderr index 27abbadd7ad24..b9c1a75276c1f 100644 --- a/src/test/ui/parser/else-no-if.stderr +++ b/src/test/ui/parser/else-no-if.stderr @@ -6,14 +6,10 @@ LL | } else false { | | | expected an `if` or a block after this `else` | -help: add an `if` if this is the condition to an chained `if` statement after the `else` +help: add an `if` if this is the condition of a chained `else if` statement | LL | } else if false { | ++ -help: ... otherwise, place this expression inside of a block if it is not an `if` condition - | -LL | } else { false } { - | + + error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:10:12 @@ -23,14 +19,10 @@ LL | } else falsy() { | | | expected an `if` or a block after this `else` | -help: add an `if` if this is the condition to an chained `if` statement after the `else` +help: add an `if` if this is the condition of a chained `else if` statement | LL | } else if falsy() { | ++ -help: ... otherwise, place this expression inside of a block if it is not an `if` condition - | -LL | } else { falsy() } { - | + + error: expected `{`, found `falsy` --> $DIR/else-no-if.rs:17:12