diff options
47 files changed, 1173 insertions, 469 deletions
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs index 538bfc21290..a7c23dbb79c 100644 --- a/compiler/rustc_ast/src/lib.rs +++ b/compiler/rustc_ast/src/lib.rs @@ -9,6 +9,8 @@ test(attr(deny(warnings))) )] #![feature(box_patterns)] +#![feature(const_default_impls)] +#![feature(const_trait_impl)] #![feature(crate_visibility_modifier)] #![feature(if_let_guard)] #![feature(label_break_value)] diff --git a/compiler/rustc_ast/src/ptr.rs b/compiler/rustc_ast/src/ptr.rs index 70dbda82224..89a0857992e 100644 --- a/compiler/rustc_ast/src/ptr.rs +++ b/compiler/rustc_ast/src/ptr.rs @@ -128,14 +128,7 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for P<T> { impl<T> P<[T]> { pub const fn new() -> P<[T]> { - // HACK(eddyb) bypass the lack of a `const fn` to create an empty `Box<[T]>` - // (as trait methods, `default` in this case, can't be `const fn` yet). - P { - ptr: unsafe { - use std::ptr::NonNull; - std::mem::transmute(NonNull::<[T; 0]>::dangling() as NonNull<[T]>) - }, - } + P { ptr: Box::default() } } #[inline(never)] diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 58a7f6d1be0..01af9585135 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -3,15 +3,14 @@ use rustc_index::bit_set::BitSet; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::mir::interpret::Scalar; -use rustc_middle::mir::traversal; use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::{ - AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPass, MirPhase, Operand, - PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, StatementKind, Terminator, - TerminatorKind, START_BLOCK, + traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, Local, Location, MirPass, + MirPhase, Operand, Place, PlaceElem, PlaceRef, ProjectionElem, Rvalue, SourceScope, Statement, + StatementKind, Terminator, TerminatorKind, UnOp, START_BLOCK, }; use rustc_middle::ty::fold::BottomUpFolder; -use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable}; use rustc_mir_dataflow::impls::MaybeStorageLive; use rustc_mir_dataflow::storage::AlwaysLiveLocals; use rustc_mir_dataflow::{Analysis, ResultsCursor}; @@ -36,6 +35,13 @@ pub struct Validator { impl<'tcx> MirPass<'tcx> for Validator { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + // FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not + // terribly important that they pass the validator. However, I think other passes might + // still see them, in which case they might be surprised. It would probably be better if we + // didn't put this through the MIR pipeline at all. + if matches!(body.source.instance, InstanceDef::Intrinsic(..) | InstanceDef::Virtual(..)) { + return; + } let def_id = body.source.def_id(); let param_env = tcx.param_env(def_id); let mir_phase = self.mir_phase; @@ -240,58 +246,179 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.super_projection_elem(local, proj_base, elem, context, location); } - fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { - match &statement.kind { - StatementKind::Assign(box (dest, rvalue)) => { - // LHS and RHS of the assignment must have the same type. - let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty; - let right_ty = rvalue.ty(&self.body.local_decls, self.tcx); - if !self.mir_assign_valid_types(right_ty, left_ty) { + fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) { + // Set off any `bug!`s in the type computation code + let _ = place.ty(&self.body.local_decls, self.tcx); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) { + macro_rules! check_kinds { + ($t:expr, $text:literal, $($patterns:tt)*) => { + if !matches!(($t).kind(), $($patterns)*) { + self.fail(location, format!($text, $t)); + } + }; + } + match rvalue { + Rvalue::Use(_) => {} + Rvalue::Aggregate(agg_kind, _) => { + let disallowed = match **agg_kind { + AggregateKind::Array(..) => false, + AggregateKind::Generator(..) => self.mir_phase >= MirPhase::GeneratorsLowered, + _ => self.mir_phase >= MirPhase::Deaggregated, + }; + if disallowed { self.fail( location, - format!( - "encountered `{:?}` with incompatible types:\n\ - left-hand side has type: {}\n\ - right-hand side has type: {}", - statement.kind, left_ty, right_ty, - ), + format!("{:?} have been lowered to field assignments", rvalue), + ) + } + } + Rvalue::Ref(_, BorrowKind::Shallow, _) => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", ); } - match rvalue { - // The sides of an assignment must not alias. Currently this just checks whether the places - // are identical. - Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) => { - if dest == src { + } + Rvalue::Len(p) => { + let pty = p.ty(&self.body.local_decls, self.tcx).ty; + check_kinds!( + pty, + "Cannot compute length of non-array type {:?}", + ty::Array(..) | ty::Slice(..) + ); + } + Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => { + use BinOp::*; + let a = vals.0.ty(&self.body.local_decls, self.tcx); + let b = vals.1.ty(&self.body.local_decls, self.tcx); + match op { + Offset => { + check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..)); + if b != self.tcx.types.isize && b != self.tcx.types.usize { + self.fail(location, format!("Cannot offset by non-isize type {:?}", b)); + } + } + Eq | Lt | Le | Ne | Ge | Gt => { + for x in [a, b] { + check_kinds!( + x, + "Cannot compare type {:?}", + ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::RawPtr(..) + | ty::FnPtr(..) + ) + } + // None of the possible types have lifetimes, so we can just compare + // directly + if a != b { self.fail( location, - "encountered `Assign` statement with overlapping memory", + format!("Cannot compare unequal types {:?} and {:?}", a, b), ); } } - Rvalue::Aggregate(agg_kind, _) => { - let disallowed = match **agg_kind { - AggregateKind::Array(..) => false, - AggregateKind::Generator(..) => { - self.mir_phase >= MirPhase::GeneratorsLowered - } - _ => self.mir_phase >= MirPhase::Deaggregated, - }; - if disallowed { + Shl | Shr => { + for x in [a, b] { + check_kinds!( + x, + "Cannot shift non-integer type {:?}", + ty::Uint(..) | ty::Int(..) + ) + } + } + BitAnd | BitOr | BitXor => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform bitwise op on type {:?}", + ty::Uint(..) | ty::Int(..) | ty::Bool + ) + } + if a != b { self.fail( location, - format!("{:?} have been lowered to field assignments", rvalue), - ) + format!( + "Cannot perform bitwise op on unequal types {:?} and {:?}", + a, b + ), + ); } } - Rvalue::Ref(_, BorrowKind::Shallow, _) => { - if self.mir_phase >= MirPhase::DropsLowered { + Add | Sub | Mul | Div | Rem => { + for x in [a, b] { + check_kinds!( + x, + "Cannot perform op on type {:?}", + ty::Uint(..) | ty::Int(..) | ty::Float(..) + ) + } + if a != b { self.fail( location, - "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", + format!("Cannot perform op on unequal types {:?} and {:?}", a, b), ); } } - _ => {} + } + } + Rvalue::UnaryOp(op, operand) => { + let a = operand.ty(&self.body.local_decls, self.tcx); + match op { + UnOp::Neg => { + check_kinds!(a, "Cannot negate type {:?}", ty::Int(..) | ty::Float(..)) + } + UnOp::Not => { + check_kinds!( + a, + "Cannot binary not type {:?}", + ty::Int(..) | ty::Uint(..) | ty::Bool + ); + } + } + } + Rvalue::ShallowInitBox(operand, _) => { + let a = operand.ty(&self.body.local_decls, self.tcx); + check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..)); + } + _ => {} + } + self.super_rvalue(rvalue, location); + } + + fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { + match &statement.kind { + StatementKind::Assign(box (dest, rvalue)) => { + // LHS and RHS of the assignment must have the same type. + let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty; + let right_ty = rvalue.ty(&self.body.local_decls, self.tcx); + if !self.mir_assign_valid_types(right_ty, left_ty) { + self.fail( + location, + format!( + "encountered `{:?}` with incompatible types:\n\ + left-hand side has type: {}\n\ + right-hand side has type: {}", + statement.kind, left_ty, right_ty, + ), + ); + } + // FIXME(JakobDegen): Check this for all rvalues, not just this one. + if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue { + // The sides of an assignment must not alias. Currently this just checks whether + // the places are identical. + if dest == src { + self.fail( + location, + "encountered `Assign` statement with overlapping memory", + ); + } } } StatementKind::AscribeUserType(..) => { @@ -512,6 +639,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { + if self.body.generator.is_none() { + self.fail(location, "`Yield` cannot appear outside generator bodies"); + } if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail(location, "`Yield` should have been replaced by generator lowering"); } @@ -551,6 +681,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::GeneratorDrop => { + if self.body.generator.is_none() { + self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies"); + } if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail( location, @@ -558,11 +691,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - // Nothing to validate for these. - TerminatorKind::Resume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable => {} + TerminatorKind::Resume | TerminatorKind::Abort => { + let bb = location.block; + if !self.body.basic_blocks()[bb].is_cleanup { + self.fail(location, "Cannot `Resume` or `Abort` from non-cleanup basic block") + } + } + TerminatorKind::Return => { + let bb = location.block; + if self.body.basic_blocks()[bb].is_cleanup { + self.fail(location, "Cannot `Return` from cleanup basic block") + } + } + TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index 88f3b547605..b33e6b66117 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -11,7 +11,7 @@ use std::error::Error; use std::fmt; use std::fs; use std::io; -use std::path::Path; +use std::path::{Path, PathBuf}; use tracing::{instrument, trace}; #[cfg(parallel_compiler)] @@ -45,7 +45,7 @@ pub enum TranslationBundleError { /// Failed to add `FluentResource` to `FluentBundle`. AddResource(FluentError), /// `$sysroot/share/locale/$locale` does not exist. - MissingLocale(io::Error), + MissingLocale, /// Cannot read directory entries of `$sysroot/share/locale/$locale`. ReadLocalesDir(io::Error), /// Cannot read directory entry of `$sysroot/share/locale/$locale`. @@ -62,9 +62,7 @@ impl fmt::Display for TranslationBundleError { write!(f, "could not parse ftl file: {}", e) } TranslationBundleError::AddResource(e) => write!(f, "failed to add resource: {}", e), - TranslationBundleError::MissingLocale(e) => { - write!(f, "missing locale directory: {}", e) - } + TranslationBundleError::MissingLocale => write!(f, "missing locale directory"), TranslationBundleError::ReadLocalesDir(e) => { write!(f, "could not read locales dir: {}", e) } @@ -84,7 +82,7 @@ impl Error for TranslationBundleError { TranslationBundleError::ReadFtl(e) => Some(e), TranslationBundleError::ParseFtl(e) => Some(e), TranslationBundleError::AddResource(e) => Some(e), - TranslationBundleError::MissingLocale(e) => Some(e), + TranslationBundleError::MissingLocale => None, TranslationBundleError::ReadLocalesDir(e) => Some(e), TranslationBundleError::ReadLocalesDirEntry(e) => Some(e), TranslationBundleError::LocaleIsNotDir => None, @@ -113,7 +111,8 @@ impl From<Vec<FluentError>> for TranslationBundleError { /// (overriding any conflicting messages). #[instrument(level = "trace")] pub fn fluent_bundle( - sysroot: &Path, + mut user_provided_sysroot: Option<PathBuf>, + mut sysroot_candidates: Vec<PathBuf>, requested_locale: Option<LanguageIdentifier>, additional_ftl_path: Option<&Path>, with_directionality_markers: bool, @@ -140,33 +139,43 @@ pub fn fluent_bundle( // If the user requests the default locale then don't try to load anything. if !requested_fallback_locale && let Some(requested_locale) = requested_locale { - let mut sysroot = sysroot.to_path_buf(); - sysroot.push("share"); - sysroot.push("locale"); - sysroot.push(requested_locale.to_string()); - trace!(?sysroot); - - let _ = sysroot.try_exists().map_err(TranslationBundleError::MissingLocale)?; - - if !sysroot.is_dir() { - return Err(TranslationBundleError::LocaleIsNotDir); - } - - for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { - let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; - let path = entry.path(); - trace!(?path); - if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + let mut found_resources = false; + for sysroot in user_provided_sysroot.iter_mut().chain(sysroot_candidates.iter_mut()) { + sysroot.push("share"); + sysroot.push("locale"); + sysroot.push(requested_locale.to_string()); + trace!(?sysroot); + + if !sysroot.exists() { trace!("skipping"); continue; } - let resource_str = - fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; - let resource = - FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; - trace!(?resource); - bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + if !sysroot.is_dir() { + return Err(TranslationBundleError::LocaleIsNotDir); + } + + for entry in sysroot.read_dir().map_err(TranslationBundleError::ReadLocalesDir)? { + let entry = entry.map_err(TranslationBundleError::ReadLocalesDirEntry)?; + let path = entry.path(); + trace!(?path); + if path.extension().and_then(|s| s.to_str()) != Some("ftl") { + trace!("skipping"); + continue; + } + + let resource_str = + fs::read_to_string(path).map_err(TranslationBundleError::ReadFtl)?; + let resource = + FluentResource::try_new(resource_str).map_err(TranslationBundleError::from)?; + trace!(?resource); + bundle.add_resource(resource).map_err(TranslationBundleError::from)?; + found_resources = true; + } + } + + if !found_resources { + return Err(TranslationBundleError::MissingLocale); } } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index ce243b4a672..b5f56d7d6dc 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -81,22 +81,12 @@ use rustc_session::parse::ParseSess; use rustc_span::symbol::MacroRulesNormalizedIdent; use rustc_span::Span; -use smallvec::{smallvec, SmallVec}; - use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_span::symbol::Ident; use std::borrow::Cow; use std::collections::hash_map::Entry::{Occupied, Vacant}; -// One element is enough to cover 95-99% of vectors for most benchmarks. Also, vectors longer than -// one frequently have many elements, not just two or three. -type NamedMatchVec = SmallVec<[NamedMatch; 1]>; - -// This type is used a lot. Make sure it doesn't unintentionally get bigger. -#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(NamedMatchVec, 48); - /// A unit within a matcher that a `MatcherPos` can refer to. Similar to (and derived from) /// `mbe::TokenTree`, but designed specifically for fast and easy traversal during matching. /// Notable differences to `mbe::TokenTree`: @@ -221,7 +211,11 @@ struct MatcherPos { /// with one element per metavar decl in the matcher. Each element records token trees matched /// against the relevant metavar by the black box parser. An element will be a `MatchedSeq` if /// the corresponding metavar decl is within a sequence. - matches: Lrc<NamedMatchVec>, + /// + /// It is critical to performance that this is an `Lrc`, because it gets cloned frequently when + /// processing sequences. Mostly for sequence-ending possibilities that must be tried but end + /// up failing. + matches: Lrc<Vec<NamedMatch>>, } // This type is used a lot. Make sure it doesn't unintentionally get bigger. @@ -246,18 +240,12 @@ impl MatcherPos { let mut curr = &mut matches[metavar_idx]; for _ in 0..seq_depth - 1 { match curr { - MatchedSeq(seq) => { - let seq = Lrc::make_mut(seq); - curr = seq.last_mut().unwrap(); - } + MatchedSeq(seq) => curr = seq.last_mut().unwrap(), _ => unreachable!(), } } match curr { - MatchedSeq(seq) => { - let seq = Lrc::make_mut(seq); - seq.push(m); - } + MatchedSeq(seq) => seq.push(m), _ => unreachable!(), } } @@ -350,7 +338,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize { /// ``` #[derive(Debug, Clone)] crate enum NamedMatch { - MatchedSeq(Lrc<NamedMatchVec>), + MatchedSeq(Vec<NamedMatch>), // A metavar match of type `tt`. MatchedTokenTree(rustc_ast::tokenstream::TokenTree), @@ -388,7 +376,7 @@ pub struct TtParser { /// Pre-allocate an empty match array, so it can be cloned cheaply for macros with many rules /// that have no metavars. - empty_matches: Lrc<NamedMatchVec>, + empty_matches: Lrc<Vec<NamedMatch>>, } impl TtParser { @@ -398,7 +386,7 @@ impl TtParser { cur_mps: vec![], next_mps: vec![], bb_mps: vec![], - empty_matches: Lrc::new(smallvec![]), + empty_matches: Lrc::new(vec![]), } } @@ -452,11 +440,7 @@ impl TtParser { } => { // Install an empty vec for each metavar within the sequence. for metavar_idx in next_metavar..next_metavar + num_metavar_decls { - mp.push_match( - metavar_idx, - seq_depth, - MatchedSeq(self.empty_matches.clone()), - ); + mp.push_match(metavar_idx, seq_depth, MatchedSeq(vec![])); } if op == KleeneOp::ZeroOrMore || op == KleeneOp::ZeroOrOne { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 31dae6a2fb4..f5c7186bc4b 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -439,7 +439,8 @@ pub fn compile_declarative_macro( let argument_gram = mbe::macro_parser::compute_locs(&sess.parse_sess, &argument_gram); let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS); - let mut tt_parser = TtParser::new(def.ident); + let mut tt_parser = + TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro })); let argument_map = match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) { Success(m) => m, Failure(token, msg) => { diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 7c53f839a92..e588385cfca 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -310,7 +310,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ // Crate properties: ungated!(crate_name, CrateLevel, template!(NameValueStr: "name"), FutureWarnFollowing), - ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), FutureWarnFollowing), + ungated!(crate_type, CrateLevel, template!(NameValueStr: "bin|lib|..."), DuplicatesOk), // crate_id is deprecated ungated!(crate_id, CrateLevel, template!(NameValueStr: "ignored"), FutureWarnFollowing), diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index a3570320767..fe75ee8b37b 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -44,6 +44,7 @@ fn mk_session(matches: getopts::Matches) -> (Session, CfgSpecs) { let sess = build_session( sessopts, None, + None, registry, DiagnosticOutput::Default, Default::default(), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 592cf60e6c3..3fa8017dc93 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -83,9 +83,23 @@ pub fn create_session( // target_override is documented to be called before init(), so this is okay let target_override = codegen_backend.target_override(&sopts); + let bundle = match rustc_errors::fluent_bundle( + sopts.maybe_sysroot.clone(), + sysroot_candidates(), + sopts.debugging_opts.translate_lang.clone(), + sopts.debugging_opts.translate_additional_ftl.as_deref(), + sopts.debugging_opts.translate_directionality_markers, + ) { + Ok(bundle) => bundle, + Err(e) => { + early_error(sopts.error_format, &format!("failed to load fluent bundle: {e}")); + } + }; + let mut sess = session::build_session( sopts, input_path, + bundle, descriptions, diagnostic_output, lint_caps, diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index fa2dad5ce25..fd2b5f5335f 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -59,6 +59,7 @@ #![feature(unwrap_infallible)] #![feature(decl_macro)] #![feature(drain_filter)] +#![feature(intra_doc_pointers)] #![recursion_limit = "512"] #![allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 578fcd82ad6..9f7832c8a64 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -127,12 +127,24 @@ pub trait MirPass<'tcx> { /// The various "big phases" that MIR goes through. /// /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the -/// dialects forbid certain variants or values in certain phases. +/// dialects forbid certain variants or values in certain phases. The sections below summarize the +/// changes, but do not document them thoroughly. The full documentation is found in the appropriate +/// documentation for the thing the change is affecting. /// /// Warning: ordering of variants is significant. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable)] pub enum MirPhase { + /// The dialect of MIR used during all phases before `DropsLowered` is the same. This is also + /// the MIR that analysis such as borrowck uses. + /// + /// One important thing to remember about the behavior of this section of MIR is that drop terminators + /// (including drop and replace) are *conditional*. The elaborate drops pass will then replace each + /// instance of a drop terminator with a nop, an unconditional drop, or a drop conditioned on a drop + /// flag. Of course, this means that it is important that the drop elaboration can accurately recognize + /// when things are initialized and when things are de-initialized. That means any code running on this + /// version of MIR must be sure to produce output that drop elaboration can reason about. See the + /// section on the drop terminatorss for more details. Built = 0, // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // We used to have this for pre-miri MIR based const eval. @@ -162,6 +174,16 @@ pub enum MirPhase { /// And the following variant is allowed: /// * [`StatementKind::SetDiscriminant`] Deaggregated = 4, + /// Before this phase, generators are in the "source code" form, featuring `yield` statements + /// and such. With this phase change, they are transformed into a proper state machine. Running + /// optimizations before this change can be potentially dangerous because the source code is to + /// some extent a "lie." In particular, `yield` terminators effectively make the value of all + /// locals visible to the caller. This means that dead store elimination before them, or code + /// motion across them, is not correct in general. This is also exasperated by type checking + /// having pre-computed a list of the types that it thinks are ok to be live across a yield + /// point - this is necessary to decide eg whether autotraits are implemented. Introducing new + /// types across a yield point will lead to ICEs becaues of this. + /// /// Beginning with this phase, the following variants are disallowed: /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) @@ -1573,18 +1595,45 @@ impl Statement<'_> { /// causing an ICE if they are violated. #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { - /// Write the RHS Rvalue to the LHS Place. + /// Assign statements roughly correspond to an assignment in Rust proper (`x = ...`) except + /// without the possibility of dropping the previous value (that must be done separately, if at + /// all). The *exact* way this works is undecided. It probably does something like evaluating + /// the LHS to a place and the RHS to a value, and then storing the value to the place. Various + /// parts of this may do type specific things that are more complicated than simply copying + /// bytes. + /// + /// **Needs clarification**: The implication of the above idea would be that assignment implies + /// that the resulting value is initialized. I believe we could commit to this separately from + /// committing to whatever part of the memory model we would need to decide on to make the above + /// paragragh precise. Do we want to? + /// + /// Assignments in which the types of the place and rvalue differ are not well-formed. + /// + /// **Needs clarification**: Do we ever want to worry about non-free (in the body) lifetimes for + /// the typing requirement in post drop-elaboration MIR? I think probably not - I'm not sure we + /// could meaningfully require this anyway. How about free lifetimes? Is ignoring this + /// interesting for optimizations? Do we want to allow such optimizations? /// - /// The LHS place may not overlap with any memory accessed on the RHS. + /// **Needs clarification**: We currently require that the LHS place not overlap with any place + /// read as part of computation of the RHS for some rvalues (generally those not producing + /// primitives). This requirement is under discussion in [#68364]. As a part of this discussion, + /// it is also unclear in what order the components are evaluated. + /// + /// [#68364]: https://github.com/rust-lang/rust/issues/68364 + /// + /// See [`Rvalue`] documentation for details on each of those. Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), - /// This represents all the reading that a pattern match may do - /// (e.g., inspecting constants and discriminant values), and the - /// kind of pattern it comes from. This is in order to adapt potential - /// error messages to these specific patterns. + /// This represents all the reading that a pattern match may do (e.g., inspecting constants and + /// discriminant values), and the kind of pattern it comes from. This is in order to adapt + /// potential error messages to these specific patterns. /// /// Note that this also is emitted for regular `let` bindings to ensure that locals that are /// never accessed still get some sanity checks for, e.g., `let x: ! = ..;` + /// + /// When executed at runtime this is a nop. + /// + /// Disallowed after drop elaboration. FakeRead(Box<(FakeReadCause, Place<'tcx>)>), /// Write the discriminant for a variant to the enum Place. @@ -1599,17 +1648,35 @@ pub enum StatementKind<'tcx> { /// This writes `uninit` bytes to the entire place. Deinit(Box<Place<'tcx>>), - /// Start a live range for the storage of the local. + /// `StorageLive` and `StorageDead` statements mark the live range of a local. + /// + /// Using a local before a `StorageLive` or after a `StorageDead` is not well-formed. These + /// statements are not required. If the entire MIR body contains no `StorageLive`/`StorageDead` + /// statements for a particular local, the local is always considered live. + /// + /// More precisely, the MIR validator currently does a `MaybeStorageLiveLocals` analysis to + /// check validity of each use of a local. I believe this is equivalent to requiring for every + /// use of a local, there exist at least one path from the root to that use that contains a + /// `StorageLive` more recently than a `StorageDead`. + /// + /// **Needs clarification**: Is it permitted to have two `StorageLive`s without an intervening + /// `StorageDead`? Two `StorageDead`s without an intervening `StorageLive`? LLVM says poison, + /// yes. If the answer to any of these is "no," is breaking that rule UB or is it an error to + /// have a path in the CFG that might do this? StorageLive(Local), - /// End the current live range for the storage of the local. + /// See `StorageLive` above. StorageDead(Local), - /// Retag references in the given place, ensuring they got fresh tags. This is - /// part of the Stacked Borrows model. These statements are currently only interpreted - /// by miri and only generated when "-Z mir-emit-retag" is passed. - /// See <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/> - /// for more details. + /// Retag references in the given place, ensuring they got fresh tags. + /// + /// This is part of the Stacked Borrows model. These statements are currently only interpreted + /// by miri and only generated when `-Z mir-emit-retag` is passed. See + /// <https://internals.rust-lang.org/t/stacked-borrows-an-aliasing-model-for-rust/8153/> for + /// more details. + /// + /// For code that is not specific to stacked borrows, you should consider retags to read + /// and modify the place in an opaque way. Retag(RetagKind, Box<Place<'tcx>>), /// Encodes a user's type ascription. These need to be preserved @@ -1624,6 +1691,10 @@ pub enum StatementKind<'tcx> { /// - `Contravariant` -- requires that `T_y :> T` /// - `Invariant` -- requires that `T_y == T` /// - `Bivariant` -- no effect + /// + /// When executed at runtime this is a nop. + /// + /// Disallowed after drop elaboration. AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance), /// Marks the start of a "coverage region", injected with '-Cinstrument-coverage'. A @@ -1633,9 +1704,19 @@ pub enum StatementKind<'tcx> { /// executed. Coverage(Box<Coverage>), - /// Denotes a call to the intrinsic function copy_overlapping, where `src_dst` denotes the - /// memory being read from and written to(one field to save memory), and size - /// indicates how many bytes are being copied over. + /// Denotes a call to the intrinsic function `copy_nonoverlapping`. + /// + /// First, all three operands are evaluated. `src` and `dest` must each be a reference, pointer, + /// or `Box` pointing to the same type `T`. `count` must evaluate to a `usize`. Then, `src` and + /// `dest` are dereferenced, and `count * size_of::<T>()` bytes beginning with the first byte of + /// the `src` place are copied to the continguous range of bytes beginning with the first byte + /// of `dest`. + /// + /// **Needs clarification**: In what order are operands computed and dereferenced? It should + /// probably match the order for assignment, but that is also undecided. + /// + /// **Needs clarification**: Is this typed or not, ie is there a typed load and store involved? + /// I vaguely remember Ralf saying somewhere that he thought it should not be. CopyNonOverlapping(Box<CopyNonOverlapping<'tcx>>), /// No-op. Useful for deleting instructions without affecting statement indices. @@ -1785,8 +1866,82 @@ pub struct CopyNonOverlapping<'tcx> { /////////////////////////////////////////////////////////////////////////// // Places -/// A path to a value; something that can be evaluated without -/// changing or disturbing program state. +/// Places roughly correspond to a "location in memory." Places in MIR are the same mathematical +/// object as places in Rust. This of course means that what exactly they are is undecided and part +/// of the Rust memory model. However, they will likely contain at least the following pieces of +/// information in some form: +/// +/// 1. The address in memory that the place refers to. +/// 2. The provenance with which the place is being accessed. +/// 3. The type of the place and an optional variant index. See [`PlaceTy`][tcx::PlaceTy]. +/// 4. Optionally, some metadata. This exists if and only if the type of the place is not `Sized`. +/// +/// We'll give a description below of how all pieces of the place except for the provenance are +/// calculated. We cannot give a description of the provenance, because that is part of the +/// undecided aliasing model - we only include it here at all to acknowledge its existence. +/// +/// Each local naturally corresponds to the place `Place { local, projection: [] }`. This place has +/// the address of the local's allocation and the type of the local. +/// +/// **Needs clarification:** Unsized locals seem to present a bit of an issue. Their allocation +/// can't actually be created on `StorageLive`, because it's unclear how big to make the allocation. +/// Furthermore, MIR produces assignments to unsized locals, although that is not permitted under +/// `#![feature(unsized_locals)]` in Rust. Besides just putting "unsized locals are special and +/// different" in a bunch of places, I (JakobDegen) don't know how to incorporate this behavior into +/// the current MIR semantics in a clean way - possibly this needs some design work first. +/// +/// For places that are not locals, ie they have a non-empty list of projections, we define the +/// values as a function of the parent place, that is the place with its last [`ProjectionElem`] +/// stripped. The way this is computed of course depends on the kind of that last projection +/// element: +/// +/// - [`Downcast`](ProjectionElem::Downcast): This projection sets the place's variant index to the +/// given one, and makes no other changes. A `Downcast` projection on a place with its variant +/// index already set is not well-formed. +/// - [`Field`](ProjectionElem::Field): `Field` projections take their parent place and create a +/// place referring to one of the fields of the type. The resulting address is the parent +/// address, plus the offset of the field. The type becomes the type of the field. If the parent +/// was unsized and so had metadata associated with it, then the metadata is retained if the +/// field is unsized and thrown out if it is sized. +/// +/// These projections are only legal for tuples, ADTs, closures, and generators. If the ADT or +/// generator has more than one variant, the parent place's variant index must be set, indicating +/// which variant is being used. If it has just one variant, the variant index may or may not be +/// included - the single possible variant is inferred if it is not included. +/// - [`ConstantIndex`](ProjectionElem::ConstantIndex): Computes an offset in units of `T` into the +/// place as described in the documentation for the `ProjectionElem`. The resulting address is +/// the parent's address plus that offset, and the type is `T`. This is only legal if the parent +/// place has type `[T; N]` or `[T]` (*not* `&[T]`). Since such a `T` is always sized, any +/// resulting metadata is thrown out. +/// - [`Subslice`](ProjectionElem::Subslice): This projection calculates an offset and a new +/// address in a similar manner as `ConstantIndex`. It is also only legal on `[T; N]` and `[T]`. +/// However, this yields a `Place` of type `[T]`, and additionally sets the metadata to be the +/// length of the subslice. +/// - [`Index`](ProjectionElem::Index): Like `ConstantIndex`, only legal on `[T; N]` or `[T]`. +/// However, `Index` additionally takes a local from which the value of the index is computed at +/// runtime. Computing the value of the index involves interpreting the `Local` as a +/// `Place { local, projection: [] }`, and then computing its value as if done via +/// [`Operand::Copy`]. The array/slice is then indexed with the resulting value. The local must +/// have type `usize`. +/// - [`Deref`](ProjectionElem::Deref): Derefs are the last type of projection, and the most +/// complicated. They are only legal on parent places that are references, pointers, or `Box`. A +/// `Deref` projection begins by loading a value from the parent place, as if by +/// [`Operand::Copy`]. It then dereferences the resulting pointer, creating a place of the +/// pointee's type. The resulting address is the address that was stored in the pointer. If the +/// pointee type is unsized, the pointer additionally stored the value of the metadata. +/// +/// Computing a place may cause UB. One possibility is that the pointer used for a `Deref` may not +/// be suitably aligned. Another possibility is that the place is not in bounds, meaning it does not +/// point to an actual allocation. +/// +/// However, if this is actually UB and when the UB kicks in is undecided. This is being discussed +/// in [UCG#319]. The options include that every place must obey those rules, that only some places +/// must obey them, or that places impose no rules of their own. +/// +/// [UCG#319]: https://github.com/rust-lang/unsafe-code-guidelines/issues/319 +/// +/// Rust currently requires that every place obey those two rules. This is checked by MIRI and taken +/// advantage of by codegen (via `gep inbounds`). That is possibly subject to change. #[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, HashStable)] pub struct Place<'tcx> { pub local: Local, @@ -2155,24 +2310,39 @@ pub struct SourceScopeLocalData { /////////////////////////////////////////////////////////////////////////// // Operands -/// These are values that can appear inside an rvalue. They are intentionally -/// limited to prevent rvalues from being nested in one another. +/// An operand in MIR represents a "value" in Rust, the definition of which is undecided and part of +/// the memory model. One proposal for a definition of values can be found [on UCG][value-def]. +/// +/// [value-def]: https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/value-domain.md +/// +/// The most common way to create values is via loading a place. Loading a place is an operation +/// which reads the memory of the place and converts it to a value. This is a fundamentally *typed* +/// operation. The nature of the value produced depends on the type of the conversion. Furthermore, +/// there may be other effects: if the type has a validity constraint loading the place might be UB +/// if the validity constraint is not met. +/// +/// **Needs clarification:** Ralf proposes that loading a place not have side-effects. +/// This is what is implemented in miri today. Are these the semantics we want for MIR? Is this +/// something we can even decide without knowing more about Rust's memory model? +/// +/// **Needs clarifiation:** Is loading a place that has its variant index set well-formed? Miri +/// currently implements it, but it seems like this may be something to check against in the +/// validator. #[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] pub enum Operand<'tcx> { - /// Copy: The value must be available for use afterwards. - /// - /// This implies that the type of the place must be `Copy`; this is true - /// by construction during build, but also checked by the MIR type checker. + /// Creates a value by loading the given place. The type of the place must be `Copy` Copy(Place<'tcx>), - /// Move: The value (including old borrows of it) will not be used again. + /// Creates a value by performing loading the place, just like the `Copy` operand. + /// + /// This *may* additionally overwrite the place with `uninit` bytes, depending on how we decide + /// in [UCG#188]. You should not emit MIR that may attempt a subsequent second load of this + /// place without first re-initializing it. /// - /// Safe for values of all types (modulo future developments towards `?Move`). - /// Correct usage patterns are enforced by the borrow checker for safe code. - /// `Copy` may be converted to `Move` to enable "last-use" optimizations. + /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188 Move(Place<'tcx>), - /// Synthesizes a constant value. + /// Constants are already semantically values, and remain unchanged. Constant(Box<Constant<'tcx>>), } @@ -2280,57 +2450,134 @@ impl<'tcx> Operand<'tcx> { #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] /// The various kinds of rvalues that can appear in MIR. /// -/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which -/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, -/// causing an ICE if they are violated. +/// Not all of these are allowed at every [`MirPhase`] - when this is the case, it's stated below. +/// +/// Computing any rvalue begins by evaluating the places and operands in some order (**Needs +/// clarification**: Which order?). These are then used to produce a "value" - the same kind of +/// value that an [`Operand`] produces. pub enum Rvalue<'tcx> { - /// x (either a move or copy, depending on type of x) + /// Yields the operand unchanged Use(Operand<'tcx>), - /// [x; 32] + /// Creates an array where each element is the value of the operand. + /// + /// This is the cause of a bug in the case where the repetition count is zero because the value + /// is not dropped, see [#74836]. + /// + /// Corresponds to source code like `[x; 32]`. + /// + /// [#74836]: https://github.com/rust-lang/rust/issues/74836 Repeat(Operand<'tcx>, ty::Const<'tcx>), - /// &x or &mut x + /// Creates a reference of the indicated kind to the place. + /// + /// There is not much to document here, because besides the obvious parts the semantics of this + /// are essentially entirely a part of the aliasing model. There are many UCG issues discussing + /// exactly what the behavior of this operation should be. + /// + /// `Shallow` borrows are disallowed after drop lowering. Ref(Region<'tcx>, BorrowKind, Place<'tcx>), - /// Accessing a thread local static. This is inherently a runtime operation, even if llvm - /// treats it as an access to a static. This `Rvalue` yields a reference to the thread local - /// static. + /// Creates a pointer/reference to the given thread local. + /// + /// The yielded type is a `*mut T` if the static is mutable, otherwise if the static is extern a + /// `*const T`, and if neither of those apply a `&T`. + /// + /// **Note:** This is a runtime operation that actually executes code and is in this sense more + /// like a function call. Also, eliminating dead stores of this rvalue causes `fn main() {}` to + /// SIGILL for some reason that I (JakobDegen) never got a chance to look into. + /// + /// **Needs clarification**: Are there weird additional semantics here related to the runtime + /// nature of this operation? ThreadLocalRef(DefId), - /// Create a raw pointer to the given place - /// Can be generated by raw address of expressions (`&raw const x`), - /// or when casting a reference to a raw pointer. + /// Creates a pointer with the indicated mutability to the place. + /// + /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like + /// `&raw v` or `addr_of!(v)`. + /// + /// Like with references, the semantics of this operation are heavily dependent on the aliasing + /// model. AddressOf(Mutability, Place<'tcx>), - /// length of a `[X]` or `[X;n]` value + /// Yields the length of the place, as a `usize`. + /// + /// If the type of the place is an array, this is the array length. For slices (`[T]`, not + /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is + /// ill-formed for places of other types. Len(Place<'tcx>), + /// Performs essentially all of the casts that can be performed via `as`. + /// + /// This allows for casts from/to a variety of types. + /// + /// **FIXME**: Document exactly which `CastKind`s allow which types of casts. Figure out why + /// `ArrayToPointer` and `MutToConstPointer` are special. Cast(CastKind, Operand<'tcx>, Ty<'tcx>), + /// * `Offset` has the same semantics as [`offset`](pointer::offset), except that the second + /// parameter may be a `usize` as well. + /// * The comparison operations accept `bool`s, `char`s, signed or unsigned integers, floats, + /// raw pointers, or function pointers of matching types and return a `bool`. + /// * Left and right shift operations accept signed or unsigned integers not necessarily of the + /// same type and return a value of the same type as their LHS. Like in Rust, the RHS is + /// truncated as needed. + /// * The `Bit*` operations accept signed integers, unsigned integers, or bools with matching + /// types and return a value of that type. + /// * The remaining operations accept signed integers, unsigned integers, or floats with + /// matching types and return a value of that type. BinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), + + /// Same as `BinaryOp`, but yields `(T, bool)` instead of `T`. In addition to performing the + /// same computation as the matching `BinaryOp`, checks if the infinite precison result would be + /// unequal to the actual result and sets the `bool` if this is the case. + /// + /// This only supports addition, subtraction, multiplication, and shift operations on integers. CheckedBinaryOp(BinOp, Box<(Operand<'tcx>, Operand<'tcx>)>), + /// Computes a value as described by the operation. NullaryOp(NullOp, Ty<'tcx>), + + /// Exactly like `BinaryOp`, but less operands. + /// + /// Also does two's-complement arithmetic. Negation requires a signed integer or a float; + /// bitwise not requires a signed integer, unsigned integer, or bool. Both operation kinds + /// return a value with the same type as their operand. UnaryOp(UnOp, Operand<'tcx>), - /// Read the discriminant of an ADT. + /// Computes the discriminant of the place, returning it as an integer of type + /// [`discriminant_ty`]. + /// + /// The validity requirements for the underlying value are undecided for this rvalue, see + /// [#91095]. Note too that the value of the discriminant is not the same thing as the + /// variant index; use [`discriminant_for_variant`] to convert. + /// + /// For types defined in the source code as enums, this is well behaved. This is also well + /// formed for other types, but yields no particular value - there is no reason it couldn't be + /// defined to yield eg zero though. /// - /// Undefined (i.e., no effort is made to make it defined, but there’s no reason why it cannot - /// be defined to return, say, a 0) if ADT is not an enum. + /// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty + /// [#91095]: https://github.com/rust-lang/rust/issues/91095 + /// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant Discriminant(Place<'tcx>), - /// Creates an aggregate value, like a tuple or struct. This is - /// only needed because we want to distinguish `dest = Foo { x: - /// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case - /// that `Foo` has a destructor. These rvalues can be optimized - /// away after type-checking and before lowering. + /// Creates an aggregate value, like a tuple or struct. + /// + /// This is needed because dataflow analysis needs to distinguish + /// `dest = Foo { x: ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case that `Foo` + /// has a destructor. + /// + /// Disallowed after deaggregation for all aggregate kinds except `Array` and `Generator`. After + /// generator lowering, `Generator` aggregate kinds are disallowed too. Aggregate(Box<AggregateKind<'tcx>>, Vec<Operand<'tcx>>), /// Transmutes a `*mut u8` into shallow-initialized `Box<T>`. /// - /// This is different a normal transmute because dataflow analysis will treat the box - /// as initialized but its content as uninitialized. + /// This is different from a normal transmute because dataflow analysis will treat the box as + /// initialized but its content as uninitialized. Like other pointer casts, this in general + /// affects alias analysis. + /// + /// Disallowed after drop elaboration. ShallowInitBox(Operand<'tcx>, Ty<'tcx>), } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index 51d8113840a..597ade42236 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -76,6 +76,9 @@ impl<'tcx> PlaceTy<'tcx> { V: ::std::fmt::Debug, T: ::std::fmt::Debug + Copy, { + if self.variant_index.is_some() && !matches!(elem, ProjectionElem::Field(..)) { + bug!("cannot use non field projection on downcasted place") + } let answer = match *elem { ProjectionElem::Deref => { let ty = self diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index ae94bd121f9..cc08857463d 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -105,13 +105,34 @@ impl<'a> Iterator for SwitchTargetsIter<'a> { impl<'a> ExactSizeIterator for SwitchTargetsIter<'a> {} +/// A note on unwinding: Panics may occur during the execution of some terminators. Depending on the +/// `-C panic` flag, this may either cause the program to abort or the call stack to unwind. Such +/// terminators have a `cleanup: Option<BasicBlock>` field on them. If stack unwinding occurs, then +/// once the current function is reached, execution continues at the given basic block, if any. If +/// `cleanup` is `None` then no cleanup is performed, and the stack continues unwinding. This is +/// equivalent to the execution of a `Resume` terminator. +/// +/// The basic block pointed to by a `cleanup` field must have its `cleanup` flag set. `cleanup` +/// basic blocks have a couple restrictions: +/// 1. All `cleanup` fields in them must be `None`. +/// 2. `Return` terminators are not allowed in them. `Abort` and `Unwind` terminators are. +/// 3. All other basic blocks (in the current body) that are reachable from `cleanup` basic blocks +/// must also be `cleanup`. This is a part of the type system and checked statically, so it is +/// still an error to have such an edge in the CFG even if it's known that it won't be taken at +/// runtime. #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] pub enum TerminatorKind<'tcx> { - /// Block should have one successor in the graph; we jump there. + /// Block has one successor; we continue execution there. Goto { target: BasicBlock }, - /// Operand evaluates to an integer; jump depending on its value - /// to one of the targets, and otherwise fallback to `otherwise`. + /// Switches based on the computed value. + /// + /// First, evaluates the `discr` operand. The type of the operand must be a signed or unsigned + /// integer, char, or bool, and must match the given type. Then, if the list of switch targets + /// contains the computed value, continues execution at the associated basic block. Otherwise, + /// continues execution at the "otherwise" basic block. + /// + /// Target values may not appear more than once. SwitchInt { /// The discriminant value being tested. discr: Operand<'tcx>, @@ -124,29 +145,62 @@ pub enum TerminatorKind<'tcx> { targets: SwitchTargets, }, - /// Indicates that the landing pad is finished and unwinding should - /// continue. Emitted by `build::scope::diverge_cleanup`. + /// Indicates that the landing pad is finished and that the process should continue unwinding. + /// + /// Like a return, this marks the end of this invocation of the function. + /// + /// Only permitted in cleanup blocks. `Resume` is not permitted with `-C unwind=abort` after + /// deaggregation runs. Resume, - /// Indicates that the landing pad is finished and that the process - /// should abort. Used to prevent unwinding for foreign items. + /// Indicates that the landing pad is finished and that the process should abort. + /// + /// Used to prevent unwinding for foreign items or with `-C unwind=abort`. Only permitted in + /// cleanup blocks. Abort, - /// Indicates a normal return. The return place should have - /// been filled in before this executes. This can occur multiple times - /// in different basic blocks. + /// Returns from the function. + /// + /// Like function calls, the exact semantics of returns in Rust are unclear. Returning very + /// likely at least assigns the value currently in the return place (`_0`) to the place + /// specified in the associated `Call` terminator in the calling function, as if assigned via + /// `dest = move _0`. It might additionally do other things, like have side-effects in the + /// aliasing model. + /// + /// If the body is a generator body, this has slightly different semantics; it instead causes a + /// `GeneratorState::Returned(_0)` to be created (as if by an `Aggregate` rvalue) and assigned + /// to the return place. Return, /// Indicates a terminator that can never be reached. + /// + /// Executing this terminator is UB. Unreachable, - /// Drop the `Place`. + /// The behavior of this statement differs significantly before and after drop elaboration. + /// After drop elaboration, `Drop` executes the drop glue for the specified place, after which + /// it continues execution/unwinds at the given basic blocks. It is possible that executing drop + /// glue is special - this would be part of Rust's memory model. (**FIXME**: due we have an + /// issue tracking if drop glue has any interesting semantics in addition to those of a function + /// call?) + /// + /// `Drop` before drop elaboration is a *conditional* execution of the drop glue. Specifically, the + /// `Drop` will be executed if... + /// + /// **Needs clarification**: End of that sentence. This in effect should document the exact + /// behavior of drop elaboration. The following sounds vaguely right, but I'm not quite sure: + /// + /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to + /// > the place or one of its "parents" occurred more recently than a move out of it. This does not + /// > consider indirect assignments. Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> }, - /// Drop the `Place` and assign the new value over it. This ensures - /// that the assignment to `P` occurs *even if* the destructor for - /// place unwinds. Its semantics are best explained by the - /// elaboration: + /// Drops the place and assigns a new value to it. + /// + /// This first performs the exact same operation as the pre drop-elaboration `Drop` terminator; + /// it then additionally assigns the `value` to the `place` as if by an assignment statement. + /// This assignment occurs both in the unwind and the regular code paths. The semantics are best + /// explained by the elaboration: /// /// ``` /// BB0 { @@ -170,7 +224,7 @@ pub enum TerminatorKind<'tcx> { /// } /// ``` /// - /// Note that DropAndReplace is eliminated as part of the `ElaborateDrops` pass. + /// Disallowed after drop elaboration. DropAndReplace { place: Place<'tcx>, value: Operand<'tcx>, @@ -178,7 +232,16 @@ pub enum TerminatorKind<'tcx> { unwind: Option<BasicBlock>, }, - /// Block ends with a call of a function. + /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of + /// the referred to function. The operand types must match the argument types of the function. + /// The return place type must match the return type. The type of the `func` operand must be + /// callable, meaning either a function pointer, a function type, or a closure type. + /// + /// **Needs clarification**: The exact semantics of this. Current backends rely on `move` + /// operands not aliasing the return place. It is unclear how this is justified in MIR, see + /// [#71117]. + /// + /// [#71117]: https://github.com/rust-lang/rust/issues/71117 Call { /// The function that’s being called. func: Operand<'tcx>, @@ -187,7 +250,7 @@ pub enum TerminatorKind<'tcx> { /// This allows the memory occupied by "by-value" arguments to be /// reused across function calls without duplicating the contents. args: Vec<Operand<'tcx>>, - /// Destination for the return value. If some, the call is converging. + /// Destination for the return value. If none, the call necessarily diverges. destination: Option<(Place<'tcx>, BasicBlock)>, /// Cleanups to be done if the call unwinds. cleanup: Option<BasicBlock>, @@ -199,8 +262,12 @@ pub enum TerminatorKind<'tcx> { fn_span: Span, }, - /// Jump to the target if the condition has the expected value, - /// otherwise panic with a message and a cleanup target. + /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`, + /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some + /// unspecified constant as the function to call, all the operands stored in the `AssertMessage` + /// as parameters, and `None` for the destination. Keep in mind that the `cleanup` path is not + /// necessarily executed even in the case of a panic, for example in `-C panic=abort`. If the + /// assertion does not fail, execution continues at the specified basic block. Assert { cond: Operand<'tcx>, expected: bool, @@ -209,7 +276,18 @@ pub enum TerminatorKind<'tcx> { cleanup: Option<BasicBlock>, }, - /// A suspend point. + /// Marks a suspend point. + /// + /// Like `Return` terminators in generator bodies, this computes `value` and then a + /// `GeneratorState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to + /// the return place of the function calling this one, and execution continues in the calling + /// function. When next invoked with the same first argument, execution of this function + /// continues at the `resume` basic block, with the second argument written to the `resume_arg` + /// place. If the generator is dropped before then, the `drop` basic block is invoked. + /// + /// Not permitted in bodies that are not generator bodies, or after generator lowering. + /// + /// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`? Yield { /// The value to return. value: Operand<'tcx>, @@ -221,11 +299,24 @@ pub enum TerminatorKind<'tcx> { drop: Option<BasicBlock>, }, - /// Indicates the end of the dropping of a generator. + /// Indicates the end of dropping a generator. + /// + /// Semantically just a `return` (from the generators drop glue). Only permitted in the same situations + /// as `yield`. + /// + /// **Needs clarification**: Is that even correct? The generator drop code is always confusing + /// to me, because it's not even really in the current body. + /// + /// **Needs clarification**: Are there type system constraints on these terminators? Should + /// there be a "block type" like `cleanup` blocks for them? GeneratorDrop, - /// A block where control flow only ever takes one real path, but borrowck - /// needs to be more conservative. + /// A block where control flow only ever takes one real path, but borrowck needs to be more + /// conservative. + /// + /// At runtime this is semantically just a goto. + /// + /// Disallowed after drop elaboration. FalseEdge { /// The target normal control flow will take. real_target: BasicBlock, @@ -233,9 +324,14 @@ pub enum TerminatorKind<'tcx> { /// practice. imaginary_target: BasicBlock, }, - /// A terminator for blocks that only take one path in reality, but where we - /// reserve the right to unwind in borrowck, even if it won't happen in practice. - /// This can arise in infinite loops with no function calls for example. + + /// A terminator for blocks that only take one path in reality, but where we reserve the right + /// to unwind in borrowck, even if it won't happen in practice. This can arise in infinite loops + /// with no function calls for example. + /// + /// At runtime this is semantically just a goto. + /// + /// Disallowed after drop elaboration. FalseUnwind { /// The target normal control flow will take. real_target: BasicBlock, diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 5bf6f22b5d0..ca81921faed 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -970,7 +970,7 @@ impl<'a> Parser<'a> { } if fixed_crate_name { let fixed_name_sp = ident.span.to(idents.last().unwrap().span); - let mut fixed_name = format!("{}", ident.name); + let mut fixed_name = ident.name.to_string(); for part in idents { fixed_name.push_str(&format!("_{}", part.name)); } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 609dbd1fe1b..de2229f742d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -632,7 +632,7 @@ impl<'a> Resolver<'a> { VisResolutionError::Relative2018(span, path) => { let mut err = self.session.struct_span_err( span, - "relative paths are not supported in visibilities on 2018 edition", + "relative paths are not supported in visibilities in 2018 edition or later", ); err.span_suggestion( path.span, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 9881046ddfa..d70f89760a1 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -20,8 +20,8 @@ use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType}; use rustc_errors::json::JsonEmitter; use rustc_errors::registry::Registry; use rustc_errors::{ - fallback_fluent_bundle, fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, - EmissionGuarantee, ErrorGuaranteed, FluentBundle, MultiSpan, + fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee, + ErrorGuaranteed, FluentBundle, MultiSpan, }; use rustc_macros::HashStable_Generic; pub use rustc_span::def_id::StableCrateId; @@ -1162,6 +1162,7 @@ pub enum DiagnosticOutput { pub fn build_session( sopts: config::Options, local_crate_source_file: Option<PathBuf>, + bundle: Option<Lrc<rustc_errors::FluentBundle>>, registry: rustc_errors::registry::Registry, diagnostics_output: DiagnosticOutput, driver_lint_caps: FxHashMap<lint::LintId, lint::Level>, @@ -1214,16 +1215,17 @@ pub fn build_session( hash_kind, )); - let bundle = fluent_bundle( - &sysroot, - sopts.debugging_opts.translate_lang.clone(), - sopts.debugging_opts.translate_additional_ftl.as_deref(), - sopts.debugging_opts.translate_directionality_markers, - ) - .expect("failed to load fluent bundle"); let fallback_bundle = - fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) - .expect("failed to load fallback fluent bundle"); + match fallback_fluent_bundle(sopts.debugging_opts.translate_directionality_markers) { + Ok(bundle) => bundle, + Err(e) => { + early_error( + sopts.error_format, + &format!("failed to load fallback fluent bundle: {e}"), + ); + } + }; + let emitter = default_emitter(&sopts, registry, source_map.clone(), bundle, fallback_bundle, write_dest); diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs index e73b9c979eb..47292b3e339 100644 --- a/compiler/rustc_typeck/src/check/cast.rs +++ b/compiler/rustc_typeck/src/check/cast.rs @@ -55,6 +55,7 @@ use rustc_trait_selection::traits::error_reporting::report_object_safety_error; pub struct CastCheck<'tcx> { expr: &'tcx hir::Expr<'tcx>, expr_ty: Ty<'tcx>, + expr_span: Span, cast_ty: Ty<'tcx>, cast_span: Span, span: Span, @@ -207,7 +208,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { cast_span: Span, span: Span, ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { - let check = CastCheck { expr, expr_ty, cast_ty, cast_span, span }; + let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); + let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; // For better error messages, check for some obviously unsized // cases now. We do a more thorough check at the end, once @@ -240,15 +242,15 @@ impl<'a, 'tcx> CastCheck<'tcx> { error_span, format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty), ); - if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr.span) { + if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) { err.span_suggestion( - self.expr.span, + self.expr_span, "dereference the expression", format!("*{}", snippet), Applicability::MaybeIncorrect, ); } else { - err.span_help(self.expr.span, "dereference the expression with `*`"); + err.span_help(self.expr_span, "dereference the expression with `*`"); } err.emit(); } @@ -315,7 +317,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); if self.expr_ty.is_numeric() { - match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { Ok(snippet) => { err.span_suggestion( self.span, @@ -440,7 +442,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } if sugg_mutref { err.span_label(self.span, "invalid cast"); - err.span_note(self.expr.span, "this reference is immutable"); + err.span_note(self.expr_span, "this reference is immutable"); err.span_note(self.cast_span, "trying to cast to a mutable reference type"); } else if let Some((sugg, remove_cast)) = sugg { err.span_label(self.span, "invalid cast"); @@ -449,7 +451,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { .tcx .sess .source_map() - .span_to_snippet(self.expr.span) + .span_to_snippet(self.expr_span) .map_or(false, |snip| snip.starts_with('(')); // Very crude check to see whether the expression must be wrapped @@ -458,14 +460,14 @@ impl<'a, 'tcx> CastCheck<'tcx> { let needs_parens = !has_parens && matches!(self.expr.kind, hir::ExprKind::Cast(..)); - let mut suggestion = vec![(self.expr.span.shrink_to_lo(), sugg)]; + let mut suggestion = vec![(self.expr_span.shrink_to_lo(), sugg)]; if needs_parens { suggestion[0].1 += "("; - suggestion.push((self.expr.span.shrink_to_hi(), ")".to_string())); + suggestion.push((self.expr_span.shrink_to_hi(), ")".to_string())); } if remove_cast { suggestion.push(( - self.expr.span.shrink_to_hi().to(self.cast_span), + self.expr_span.shrink_to_hi().to(self.cast_span), String::new(), )); } @@ -481,7 +483,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { ) { let mut label = true; // Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion: - if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { if let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) { let ty = fcx.resolve_vars_if_possible(self.cast_ty); // Erase regions to avoid panic in `prove_value` when calling @@ -550,7 +552,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { if fcx.tcx.sess.is_nightly_build() { err.span_label( - self.expr.span, + self.expr_span, "consider casting this expression to `*const ()`, \ then using `core::ptr::from_raw_parts`", ); @@ -651,7 +653,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } _ => { - err.span_help(self.expr.span, "consider using a box or reference as appropriate"); + err.span_help(self.expr_span, "consider using a box or reference as appropriate"); } } err.emit() @@ -685,7 +687,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { #[instrument(skip(fcx), level = "debug")] pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { - self.expr_ty = fcx.structurally_resolved_type(self.expr.span, self.expr_ty); + self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty); self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty); debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); @@ -741,7 +743,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { ty::FnDef(..) => { // Attempt a coercion to a fn pointer type. let f = fcx.normalize_associated_types_in( - self.expr.span, + self.expr_span, self.expr_ty.fn_sig(fcx.tcx), ); let res = fcx.try_coerce( @@ -997,7 +999,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { )); let msg = "use `.addr()` to obtain the address of a pointer"; - if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { let scalar_cast = match t_c { ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(), _ => format!(" as {}", self.cast_ty), @@ -1027,13 +1029,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { self.expr.hir_id, self.span, |err| { - let mut err = err.build(&format!( "strict provenance disallows casting integer `{}` to pointer `{}`", self.expr_ty, self.cast_ty )); let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; - if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { + if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { err.span_suggestion( self.span, msg, diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs index b2be70e707d..f9664a9b991 100644 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ b/compiler/rustc_typeck/src/check/wfcheck.rs @@ -816,16 +816,69 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); - let err_ty_str; - let mut is_ptr = true; - let err = if tcx.features().adt_const_params { - match ty.peel_refs().kind() { + if tcx.features().adt_const_params { + let err = match ty.peel_refs().kind() { ty::FnPtr(_) => Some("function pointers"), ty::RawPtr(_) => Some("raw pointers"), _ => None, + }; + + if let Some(unsupported_type) = err { + tcx.sess.span_err( + hir_ty.span, + &format!( + "using {} as const generic parameters is forbidden", + unsupported_type + ), + ); + } + + if traits::search_for_structural_match_violation(param.span, tcx, ty).is_some() { + // We use the same error code in both branches, because this is really the same + // issue: we just special-case the message for type parameters to make it + // clearer. + if let ty::Param(_) = ty.peel_refs().kind() { + // Const parameters may not have type parameters as their types, + // because we cannot be sure that the type parameter derives `PartialEq` + // and `Eq` (just implementing them is not enough for `structural_match`). + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ + used as the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` may not derive both `PartialEq` and `Eq`", ty), + ) + .note( + "it is not currently possible to use a type parameter as the type of a \ + const parameter", + ) + .emit(); + } else { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ + the type of a const parameter", + ty, + ) + .span_label( + hir_ty.span, + format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), + ) + .emit(); + } } } else { - match ty.kind() { + let err_ty_str; + let mut is_ptr = true; + + let err = match ty.kind() { ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, ty::FnPtr(_) => Some("function pointers"), ty::RawPtr(_) => Some("raw pointers"), @@ -834,74 +887,33 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { err_ty_str = format!("`{}`", ty); Some(err_ty_str.as_str()) } - } - }; - if let Some(unsupported_type) = err { - if is_ptr { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {} as const generic parameters is forbidden", - unsupported_type - ), - ); - } else { - let mut err = tcx.sess.struct_span_err( - hir_ty.span, - &format!( - "{} is forbidden as the type of a const generic parameter", - unsupported_type - ), - ); - err.note("the only supported types are integers, `bool` and `char`"); - if tcx.sess.is_nightly_build() { - err.help( + }; + + if let Some(unsupported_type) = err { + if is_ptr { + tcx.sess.span_err( + hir_ty.span, + &format!( + "using {} as const generic parameters is forbidden", + unsupported_type + ), + ); + } else { + let mut err = tcx.sess.struct_span_err( + hir_ty.span, + &format!( + "{} is forbidden as the type of a const generic parameter", + unsupported_type + ), + ); + err.note("the only supported types are integers, `bool` and `char`"); + if tcx.sess.is_nightly_build() { + err.help( "more complex types are supported with `#![feature(adt_const_params)]`", ); + } + err.emit(); } - err.emit(); - } - }; - - if traits::search_for_structural_match_violation(param.span, tcx, ty).is_some() { - // We use the same error code in both branches, because this is really the same - // issue: we just special-case the message for type parameters to make it - // clearer. - if let ty::Param(_) = ty.peel_refs().kind() { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ty, - ) - .span_label( - hir_ty.span, - format!("`{}` may not derive both `PartialEq` and `Eq`", ty), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } else { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - ty, - ) - .span_label( - hir_ty.span, - format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), - ) - .emit(); } } } diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index e6faf1df3a8..639e7f213ea 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1192,17 +1192,25 @@ impl<T: Default> Default for Box<T> { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] -impl<T> Default for Box<[T]> { +#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] +impl<T> const Default for Box<[T]> { fn default() -> Self { - Box::<[T; 0]>::new([]) + let ptr: Unique<[T]> = Unique::<[T; 0]>::dangling(); + Box(ptr, Global) } } #[cfg(not(no_global_oom_handling))] #[stable(feature = "default_box_extra", since = "1.17.0")] -impl Default for Box<str> { +#[rustc_const_unstable(feature = "const_default_impls", issue = "87864")] +impl const Default for Box<str> { fn default() -> Self { - unsafe { from_boxed_utf8_unchecked(Default::default()) } + // SAFETY: This is the same as `Unique::cast<U>` but with an unsized `U = str`. + let ptr: Unique<str> = unsafe { + let bytes: Unique<[u8]> = Unique::<[u8; 0]>::dangling(); + Unique::new_unchecked(bytes.as_ptr() as *mut str) + }; + Box(ptr, Global) } } diff --git a/library/alloc/tests/const_fns.rs b/library/alloc/tests/const_fns.rs index f448b3eb7c3..49b837becbc 100644 --- a/library/alloc/tests/const_fns.rs +++ b/library/alloc/tests/const_fns.rs @@ -6,6 +6,9 @@ pub const MY_VEC2: Vec<usize> = Default::default(); pub const MY_STRING: String = String::new(); pub const MY_STRING2: String = Default::default(); +pub const MY_BOXED_SLICE: Box<[usize]> = Default::default(); +pub const MY_BOXED_STR: Box<str> = Default::default(); + use std::collections::{BTreeMap, BTreeSet}; pub const MY_BTREEMAP: BTreeMap<usize, usize> = BTreeMap::new(); @@ -23,6 +26,9 @@ fn test_const() { assert_eq!(MY_VEC, MY_VEC2); assert_eq!(MY_STRING, MY_STRING2); + assert_eq!(MY_VEC, *MY_BOXED_SLICE); + assert_eq!(MY_STRING, *MY_BOXED_STR); + assert_eq!(MAP_LEN, 0); assert_eq!(SET_LEN, 0); assert!(MAP_IS_EMPTY && SET_IS_EMPTY); diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 98c9bf556bb..f45b73b0533 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -5,6 +5,7 @@ use crate::ascii; use crate::intrinsics; use crate::mem; +use crate::ops::{Add, Mul, Sub}; use crate::str::FromStr; // Used because the `?` operator is not allowed in a const context. @@ -954,9 +955,10 @@ pub enum FpCategory { } #[doc(hidden)] -trait FromStrRadixHelper: PartialOrd + Copy { - fn min_value() -> Self; - fn max_value() -> Self; +trait FromStrRadixHelper: + PartialOrd + Copy + Add<Output = Self> + Sub<Output = Self> + Mul<Output = Self> +{ + const MIN: Self; fn from_u32(u: u32) -> Self; fn checked_mul(&self, other: u32) -> Option<Self>; fn checked_sub(&self, other: u32) -> Option<Self>; @@ -976,12 +978,9 @@ macro_rules! from_str_radix_int_impl { } from_str_radix_int_impl! { isize i8 i16 i32 i64 i128 usize u8 u16 u32 u64 u128 } -macro_rules! doit { +macro_rules! impl_helper_for { ($($t:ty)*) => ($(impl FromStrRadixHelper for $t { - #[inline] - fn min_value() -> Self { Self::MIN } - #[inline] - fn max_value() -> Self { Self::MAX } + const MIN: Self = Self::MIN; #[inline] fn from_u32(u: u32) -> Self { u as Self } #[inline] @@ -998,7 +997,18 @@ macro_rules! doit { } })*) } -doit! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } +impl_helper_for! { i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize } + +/// Determins if a string of text of that length of that radix could be guaranteed to be +/// stored in the given type T. +/// Note that if the radix is known to the compiler, it is just the check of digits.len that +/// is done at runtime. +#[doc(hidden)] +#[inline(always)] +#[unstable(issue = "none", feature = "std_internals")] +pub fn can_not_overflow<T>(radix: u32, is_signed_ty: bool, digits: &[u8]) -> bool { + radix <= 16 && digits.len() <= mem::size_of::<T>() * 2 - is_signed_ty as usize +} fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, ParseIntError> { use self::IntErrorKind::*; @@ -1014,7 +1024,7 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par return Err(PIE { kind: Empty }); } - let is_signed_ty = T::from_u32(0) > T::min_value(); + let is_signed_ty = T::from_u32(0) > T::MIN; // all valid digits are ascii, so we will just iterate over the utf8 bytes // and cast them to chars. .to_digit() will safely return None for anything @@ -1032,38 +1042,56 @@ fn from_str_radix<T: FromStrRadixHelper>(src: &str, radix: u32) -> Result<T, Par }; let mut result = T::from_u32(0); - if is_positive { - // The number is positive - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: PosOverflow }), - }; - result = match result.checked_add(x) { - Some(result) => result, - None => return Err(PIE { kind: PosOverflow }), + + if can_not_overflow::<T>(radix, is_signed_ty, digits) { + // If the len of the str is short compared to the range of the type + // we are parsing into, then we can be certain that an overflow will not occur. + // This bound is when `radix.pow(digits.len()) - 1 <= T::MAX` but the condition + // above is a faster (conservative) approximation of this. + // + // Consider radix 16 as it has the highest information density per digit and will thus overflow the earliest: + // `u8::MAX` is `ff` - any str of len 2 is guaranteed to not overflow. + // `i8::MAX` is `7f` - only a str of len 1 is guaranteed to not overflow. + macro_rules! run_unchecked_loop { + ($unchecked_additive_op:expr) => { + for &c in digits { + result = result * T::from_u32(radix); + let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; + result = $unchecked_additive_op(result, T::from_u32(x)); + } }; } + if is_positive { + run_unchecked_loop!(<T as core::ops::Add>::add) + } else { + run_unchecked_loop!(<T as core::ops::Sub>::sub) + }; } else { - // The number is negative - for &c in digits { - let x = match (c as char).to_digit(radix) { - Some(x) => x, - None => return Err(PIE { kind: InvalidDigit }), - }; - result = match result.checked_mul(radix) { - Some(result) => result, - None => return Err(PIE { kind: NegOverflow }), - }; - result = match result.checked_sub(x) { - Some(result) => result, - None => return Err(PIE { kind: NegOverflow }), + macro_rules! run_checked_loop { + ($checked_additive_op:ident, $overflow_err:expr) => { + for &c in digits { + // When `radix` is passed in as a literal, rather than doing a slow `imul` + // the compiler can use shifts if `radix` can be expressed as a + // sum of powers of 2 (x*10 can be written as x*8 + x*2). + // When the compiler can't use these optimisations, + // the latency of the multiplication can be hidden by issuing it + // before the result is needed to improve performance on + // modern out-of-order CPU as multiplication here is slower + // than the other instructions, we can get the end result faster + // doing multiplication first and let the CPU spends other cycles + // doing other computation and get multiplication result later. + let mul = result.checked_mul(radix); + let x = (c as char).to_digit(radix).ok_or(PIE { kind: InvalidDigit })?; + result = mul.ok_or_else($overflow_err)?; + result = T::$checked_additive_op(&result, x).ok_or_else($overflow_err)?; + } }; } + if is_positive { + run_checked_loop!(checked_add, || PIE { kind: PosOverflow }) + } else { + run_checked_loop!(checked_sub, || PIE { kind: NegOverflow }) + }; } Ok(result) } diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 09e2e04a1e5..447a6fcf756 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -53,6 +53,7 @@ #![feature(numfmt)] #![feature(step_trait)] #![feature(str_internals)] +#![feature(std_internals)] #![feature(test)] #![feature(trusted_len)] #![feature(try_blocks)] diff --git a/library/core/tests/num/mod.rs b/library/core/tests/num/mod.rs index 4f773a824ef..49580cdcc48 100644 --- a/library/core/tests/num/mod.rs +++ b/library/core/tests/num/mod.rs @@ -2,7 +2,7 @@ use core::cmp::PartialEq; use core::convert::{TryFrom, TryInto}; use core::fmt::Debug; use core::marker::Copy; -use core::num::{IntErrorKind, ParseIntError, TryFromIntError}; +use core::num::{can_not_overflow, IntErrorKind, ParseIntError, TryFromIntError}; use core::ops::{Add, Div, Mul, Rem, Sub}; use core::option::Option; use core::option::Option::None; @@ -121,6 +121,75 @@ fn test_int_from_str_overflow() { } #[test] +fn test_can_not_overflow() { + fn can_overflow<T>(radix: u32, input: &str) -> bool + where + T: std::convert::TryFrom<i8>, + { + !can_not_overflow::<T>(radix, T::try_from(-1_i8).is_ok(), input.as_bytes()) + } + + // Positive tests: + assert!(!can_overflow::<i8>(16, "F")); + assert!(!can_overflow::<u8>(16, "FF")); + + assert!(!can_overflow::<i8>(10, "9")); + assert!(!can_overflow::<u8>(10, "99")); + + // Negative tests: + + // Not currently in std lib (issue: #27728) + fn format_radix<T>(mut x: T, radix: T) -> String + where + T: std::ops::Rem<Output = T>, + T: std::ops::Div<Output = T>, + T: std::cmp::PartialEq, + T: std::default::Default, + T: Copy, + T: Default, + u32: TryFrom<T>, + { + let mut result = vec![]; + + loop { + let m = x % radix; + x = x / radix; + result.push( + std::char::from_digit(m.try_into().ok().unwrap(), radix.try_into().ok().unwrap()) + .unwrap(), + ); + if x == T::default() { + break; + } + } + result.into_iter().rev().collect() + } + + macro_rules! check { + ($($t:ty)*) => ($( + for base in 2..=36 { + let num = (<$t>::MAX as u128) + 1; + + // Calcutate the string length for the smallest overflowing number: + let max_len_string = format_radix(num, base as u128); + // Ensure that that string length is deemed to potentially overflow: + assert!(can_overflow::<$t>(base, &max_len_string)); + } + )*) + } + + check! { i8 i16 i32 i64 i128 isize usize u8 u16 u32 u64 } + + // Check u128 separately: + for base in 2..=36 { + let num = u128::MAX as u128; + let max_len_string = format_radix(num, base as u128); + // base 16 fits perfectly for u128 and won't overflow: + assert_eq!(can_overflow::<u128>(base, &max_len_string), base != 16); + } +} + +#[test] fn test_leading_plus() { test_parse::<u8>("+127", Ok(127)); test_parse::<i64>("+9223372036854775807", Ok(9223372036854775807)); diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index 807b057234a..e6013c7c051 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -8,7 +8,7 @@ use crate::fmt; use crate::fs; use crate::marker::PhantomData; use crate::mem::forget; -#[cfg(not(any(target_os = "wasi", target_env = "sgx")))] +#[cfg(not(any(target_arch = "wasm32", target_env = "sgx")))] use crate::sys::cvt; use crate::sys_common::{AsInner, FromInner, IntoInner}; @@ -76,7 +76,7 @@ impl BorrowedFd<'_> { impl OwnedFd { /// Creates a new `OwnedFd` instance that shares the same underlying file handle /// as the existing `OwnedFd` instance. - #[cfg(not(target_os = "wasi"))] + #[cfg(not(target_arch = "wasm32"))] pub fn try_clone(&self) -> crate::io::Result<Self> { // We want to atomically duplicate this file descriptor and set the // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This @@ -95,7 +95,7 @@ impl OwnedFd { Ok(unsafe { Self::from_raw_fd(fd) }) } - #[cfg(target_os = "wasi")] + #[cfg(target_arch = "wasm32")] pub fn try_clone(&self) -> crate::io::Result<Self> { Err(crate::io::const_io_error!( crate::io::ErrorKind::Unsupported, diff --git a/library/std/src/os/fd/raw.rs b/library/std/src/os/fd/raw.rs index f9c883dd6bf..47ee88d97fb 100644 --- a/library/std/src/os/fd/raw.rs +++ b/library/std/src/os/fd/raw.rs @@ -5,7 +5,7 @@ use crate::fs; use crate::io; use crate::os::raw; -#[cfg(doc)] +#[cfg(all(doc, not(target_arch = "wasm32")))] use crate::os::unix::io::AsFd; #[cfg(unix)] use crate::os::unix::io::OwnedFd; diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py index 54b6750cc4d..9af0657caff 100644 --- a/src/bootstrap/bootstrap.py +++ b/src/bootstrap/bootstrap.py @@ -1173,9 +1173,9 @@ class RustBuild(object): """Check that vendoring is configured properly""" vendor_dir = os.path.join(self.rust_root, 'vendor') if 'SUDO_USER' in os.environ and not self.use_vendored_sources: - if os.environ.get('USER') != os.environ['SUDO_USER']: + if os.getuid() == 0: self.use_vendored_sources = True - print('info: looks like you are running this command under `sudo`') + print('info: looks like you\'re trying to run this command as root') print(' and so in order to preserve your $HOME this will now') print(' use vendored sources by default.') if not os.path.exists(vendor_dir): diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index e4937d7bbcc..7b496e6c669 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -865,8 +865,8 @@ impl Build { } } } else { - let base = self.llvm_out(self.config.build).join("build"); - let base = if !self.ninja() && self.config.build.contains("msvc") { + let base = self.llvm_out(target).join("build"); + let base = if !self.ninja() && target.contains("msvc") { if self.config.llvm_optimize { if self.config.llvm_release_debuginfo { base.join("RelWithDebInfo") diff --git a/src/etc/pre-push.sh b/src/etc/pre-push.sh index a78725f2ab0..5f5b48bc1c0 100755 --- a/src/etc/pre-push.sh +++ b/src/etc/pre-push.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Call `tidy --bless` before each commit +# Call `tidy --bless` before git push # Copy this script to .git/hooks to activate, # and remove it from .git/hooks to deactivate. # @@ -14,6 +14,8 @@ COMMAND="$ROOT_DIR/x.py test tidy --bless" if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then COMMAND="python $COMMAND" +elif ! command -v python &> /dev/null; then + COMMAND="python3 $COMMAND" fi echo "Running pre-push script '$COMMAND'" diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 50ae22b99cd..eabe0803b49 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1069,13 +1069,7 @@ impl Tester for Collector { } } TestFailure::ExecutionFailure(out) => { - let reason = if let Some(code) = out.status.code() { - format!("exit code {code}") - } else { - String::from("terminated by signal") - }; - - eprintln!("Test executable failed ({reason})."); + eprintln!("Test executable failed ({reason}).", reason = out.status); // FIXME(#12309): An unfortunate side-effect of capturing the test // executable's output is that the relative ordering between the test's diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index 1e9a65e1d2f..7c19865b6d7 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -173,15 +173,17 @@ fn build_rule(v: &[u8], positions: &[usize]) -> String { .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("")) .collect::<String>() .trim() - .replace('\n', " ") - .replace('/', "") - .replace('\t', " ") - .replace('{', "") - .replace('}', "") + .chars() + .filter_map(|c| match c { + '\n' | '\t' => Some(' '), + '/' | '{' | '}' => None, + c => Some(c), + }) + .collect::<String>() .split(' ') .filter(|s| !s.is_empty()) - .collect::<Vec<&str>>() - .join(" "), + .intersperse(" ") + .collect::<String>(), ) .unwrap_or_else(|_| String::new()) } diff --git a/src/test/mir-opt/lower_intrinsics.rs b/src/test/mir-opt/lower_intrinsics.rs index 8a8880dad02..eab51b65f1a 100644 --- a/src/test/mir-opt/lower_intrinsics.rs +++ b/src/test/mir-opt/lower_intrinsics.rs @@ -3,7 +3,7 @@ #![crate_type = "lib"] // EMIT_MIR lower_intrinsics.wrapping.LowerIntrinsics.diff -pub fn wrapping<T: Copy>(a: T, b: T) { +pub fn wrapping(a: i32, b: i32) { let _x = core::intrinsics::wrapping_add(a, b); let _y = core::intrinsics::wrapping_sub(a, b); let _z = core::intrinsics::wrapping_mul(a, b); diff --git a/src/test/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff b/src/test/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff index a531a19bd78..5a0286bad2f 100644 --- a/src/test/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff +++ b/src/test/mir-opt/lower_intrinsics.wrapping.LowerIntrinsics.diff @@ -1,23 +1,23 @@ - // MIR for `wrapping` before LowerIntrinsics + // MIR for `wrapping` after LowerIntrinsics - fn wrapping(_1: T, _2: T) -> () { - debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:6:26: 6:27 - debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:6:32: 6:33 - let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:6:38: 6:38 - let _3: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:9: 7:11 - let mut _4: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46 - let mut _5: T; // in scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49 - let mut _7: T; // in scope 0 at $DIR/lower_intrinsics.rs:8:45: 8:46 - let mut _8: T; // in scope 0 at $DIR/lower_intrinsics.rs:8:48: 8:49 - let mut _10: T; // in scope 0 at $DIR/lower_intrinsics.rs:9:45: 9:46 - let mut _11: T; // in scope 0 at $DIR/lower_intrinsics.rs:9:48: 9:49 + fn wrapping(_1: i32, _2: i32) -> () { + debug a => _1; // in scope 0 at $DIR/lower_intrinsics.rs:6:17: 6:18 + debug b => _2; // in scope 0 at $DIR/lower_intrinsics.rs:6:25: 6:26 + let mut _0: (); // return place in scope 0 at $DIR/lower_intrinsics.rs:6:33: 6:33 + let _3: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:9: 7:11 + let mut _4: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46 + let mut _5: i32; // in scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49 + let mut _7: i32; // in scope 0 at $DIR/lower_intrinsics.rs:8:45: 8:46 + let mut _8: i32; // in scope 0 at $DIR/lower_intrinsics.rs:8:48: 8:49 + let mut _10: i32; // in scope 0 at $DIR/lower_intrinsics.rs:9:45: 9:46 + let mut _11: i32; // in scope 0 at $DIR/lower_intrinsics.rs:9:48: 9:49 scope 1 { debug _x => _3; // in scope 1 at $DIR/lower_intrinsics.rs:7:9: 7:11 - let _6: T; // in scope 1 at $DIR/lower_intrinsics.rs:8:9: 8:11 + let _6: i32; // in scope 1 at $DIR/lower_intrinsics.rs:8:9: 8:11 scope 2 { debug _y => _6; // in scope 2 at $DIR/lower_intrinsics.rs:8:9: 8:11 - let _9: T; // in scope 2 at $DIR/lower_intrinsics.rs:9:9: 9:11 + let _9: i32; // in scope 2 at $DIR/lower_intrinsics.rs:9:9: 9:11 scope 3 { debug _z => _9; // in scope 3 at $DIR/lower_intrinsics.rs:9:9: 9:11 } @@ -30,10 +30,10 @@ _4 = _1; // scope 0 at $DIR/lower_intrinsics.rs:7:45: 7:46 StorageLive(_5); // scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49 _5 = _2; // scope 0 at $DIR/lower_intrinsics.rs:7:48: 7:49 -- _3 = wrapping_add::<T>(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50 +- _3 = wrapping_add::<i32>(move _4, move _5) -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:7:14: 7:44 -- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_add::<T>}, val: Value(Scalar(<ZST>)) } +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_add::<i32>}, val: Value(Scalar(<ZST>)) } + _3 = Add(move _4, move _5); // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50 + goto -> bb1; // scope 0 at $DIR/lower_intrinsics.rs:7:14: 7:50 } @@ -46,10 +46,10 @@ _7 = _1; // scope 1 at $DIR/lower_intrinsics.rs:8:45: 8:46 StorageLive(_8); // scope 1 at $DIR/lower_intrinsics.rs:8:48: 8:49 _8 = _2; // scope 1 at $DIR/lower_intrinsics.rs:8:48: 8:49 -- _6 = wrapping_sub::<T>(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50 +- _6 = wrapping_sub::<i32>(move _7, move _8) -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:8:14: 8:44 -- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_sub::<T>}, val: Value(Scalar(<ZST>)) } +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_sub::<i32>}, val: Value(Scalar(<ZST>)) } + _6 = Sub(move _7, move _8); // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50 + goto -> bb2; // scope 1 at $DIR/lower_intrinsics.rs:8:14: 8:50 } @@ -62,10 +62,10 @@ _10 = _1; // scope 2 at $DIR/lower_intrinsics.rs:9:45: 9:46 StorageLive(_11); // scope 2 at $DIR/lower_intrinsics.rs:9:48: 9:49 _11 = _2; // scope 2 at $DIR/lower_intrinsics.rs:9:48: 9:49 -- _9 = wrapping_mul::<T>(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50 +- _9 = wrapping_mul::<i32>(move _10, move _11) -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50 - // mir::Constant - // + span: $DIR/lower_intrinsics.rs:9:14: 9:44 -- // + literal: Const { ty: extern "rust-intrinsic" fn(T, T) -> T {wrapping_mul::<T>}, val: Value(Scalar(<ZST>)) } +- // + literal: Const { ty: extern "rust-intrinsic" fn(i32, i32) -> i32 {wrapping_mul::<i32>}, val: Value(Scalar(<ZST>)) } + _9 = Mul(move _10, move _11); // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50 + goto -> bb3; // scope 2 at $DIR/lower_intrinsics.rs:9:14: 9:50 } @@ -73,7 +73,7 @@ bb3: { StorageDead(_11); // scope 2 at $DIR/lower_intrinsics.rs:9:49: 9:50 StorageDead(_10); // scope 2 at $DIR/lower_intrinsics.rs:9:49: 9:50 - _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:6:38: 10:2 + _0 = const (); // scope 0 at $DIR/lower_intrinsics.rs:6:33: 10:2 StorageDead(_9); // scope 2 at $DIR/lower_intrinsics.rs:10:1: 10:2 StorageDead(_6); // scope 1 at $DIR/lower_intrinsics.rs:10:1: 10:2 StorageDead(_3); // scope 0 at $DIR/lower_intrinsics.rs:10:1: 10:2 diff --git a/src/test/run-make/translation/Makefile b/src/test/run-make/translation/Makefile index 22a3bf57ecf..bfff75e7acb 100644 --- a/src/test/run-make/translation/Makefile +++ b/src/test/run-make/translation/Makefile @@ -15,7 +15,9 @@ normal: basic-translation.rs custom: basic-translation.rs basic-translation.ftl $(RUSTC) $< -Ztranslate-additional-ftl=$(CURDIR)/basic-translation.ftl 2>&1 | grep "this is a test message" -# Make a local copy of the sysroot and add the custom locale to it. +# Check that a locale can be loaded from the sysroot given a language +# identifier by making a local copy of the sysroot and adding the custom locale +# to it. sysroot: basic-translation.rs basic-translation.ftl mkdir $(FAKEROOT) ln -s $(SYSROOT)/* $(FAKEROOT) @@ -31,3 +33,27 @@ sysroot: basic-translation.rs basic-translation.ftl mkdir -p $(FAKEROOT)/share/locale/zh-CN/ ln -s $(CURDIR)/basic-translation.ftl $(FAKEROOT)/share/locale/zh-CN/basic-translation.ftl $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 | grep "this is a test message" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-missing: + $(RUSTC) $< -Ztranslate-lang=tlh 2>&1 || grep "missing locale directory" + +# Check that the compiler errors out when the sysroot requested cannot be +# found. This test might start failing if there actually exists a Klingon +# translation of rustc's error messages. +sysroot-invalid: basic-translation.rs basic-translation.ftl + mkdir $(FAKEROOT) + ln -s $(SYSROOT)/* $(FAKEROOT) + rm -f $(FAKEROOT)/lib + mkdir $(FAKEROOT)/lib + ln -s $(SYSROOT)/lib/* $(FAKEROOT)/lib + rm -f $(FAKEROOT)/lib/rustlib + mkdir $(FAKEROOT)/lib/rustlib + ln -s $(SYSROOT)/lib/rustlib/* $(FAKEROOT)/lib/rustlib + rm -f $(FAKEROOT)/lib/rustlib/src + mkdir $(FAKEROOT)/lib/rustlib/src + ln -s $(SYSROOT)/lib/rustlib/src/* $(FAKEROOT)/lib/rustlib/src + touch $(FAKEROOT)/share/locale/zh-CN/ + $(RUSTC) $< --sysroot $(FAKEROOT) -Ztranslate-lang=zh-CN 2>&1 || grep "`\$sysroot/share/locales/\$locale` is not a directory" diff --git a/src/test/rustdoc-ui/failed-doctest-output-windows.rs b/src/test/rustdoc-ui/failed-doctest-output-windows.rs new file mode 100644 index 00000000000..4cd9993d8d5 --- /dev/null +++ b/src/test/rustdoc-ui/failed-doctest-output-windows.rs @@ -0,0 +1,28 @@ +// only-windows +// There's a parallel generic version of this test for non-windows platforms. + +// Issue #51162: A failed doctest was not printing its stdout/stderr +// FIXME: if/when the output of the test harness can be tested on its own, this test should be +// adapted to use that, and that normalize line can go away + +// compile-flags:--test --test-args --test-threads=1 +// rustc-env:RUST_BACKTRACE=0 +// normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR" +// normalize-stdout-test "finished in \d+\.\d+s" -> "finished in $$TIME" +// failure-status: 101 + +// doctest fails at runtime +/// ``` +/// println!("stdout 1"); +/// eprintln!("stderr 1"); +/// println!("stdout 2"); +/// eprintln!("stderr 2"); +/// panic!("oh no"); +/// ``` +pub struct SomeStruct; + +// doctest fails at compile time +/// ``` +/// no +/// ``` +pub struct OtherStruct; diff --git a/src/test/rustdoc-ui/failed-doctest-output-windows.stdout b/src/test/rustdoc-ui/failed-doctest-output-windows.stdout new file mode 100644 index 00000000000..6c147054da3 --- /dev/null +++ b/src/test/rustdoc-ui/failed-doctest-output-windows.stdout @@ -0,0 +1,39 @@ + +running 2 tests +test $DIR/failed-doctest-output-windows.rs - OtherStruct (line 25) ... FAILED +test $DIR/failed-doctest-output-windows.rs - SomeStruct (line 15) ... FAILED + +failures: + +---- $DIR/failed-doctest-output-windows.rs - OtherStruct (line 25) stdout ---- +error[E0425]: cannot find value `no` in this scope + --> $DIR/failed-doctest-output-windows.rs:26:1 + | +LL | no + | ^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0425`. +Couldn't compile the test. +---- $DIR/failed-doctest-output-windows.rs - SomeStruct (line 15) stdout ---- +Test executable failed (exit code: 101). + +stdout: +stdout 1 +stdout 2 + +stderr: +stderr 1 +stderr 2 +thread 'main' panicked at 'oh no', $DIR/failed-doctest-output-windows.rs:7:1 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + + +failures: + $DIR/failed-doctest-output-windows.rs - OtherStruct (line 25) + $DIR/failed-doctest-output-windows.rs - SomeStruct (line 15) + +test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/src/test/rustdoc-ui/failed-doctest-output.rs b/src/test/rustdoc-ui/failed-doctest-output.rs index 92473b49e14..42de954d052 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.rs +++ b/src/test/rustdoc-ui/failed-doctest-output.rs @@ -1,3 +1,6 @@ +// ignore-windows +// There's a parallel version of this test for Windows. + // Issue #51162: A failed doctest was not printing its stdout/stderr // FIXME: if/when the output of the test harness can be tested on its own, this test should be // adapted to use that, and that normalize line can go away diff --git a/src/test/rustdoc-ui/failed-doctest-output.stdout b/src/test/rustdoc-ui/failed-doctest-output.stdout index 6dfe648f854..630198a561a 100644 --- a/src/test/rustdoc-ui/failed-doctest-output.stdout +++ b/src/test/rustdoc-ui/failed-doctest-output.stdout @@ -1,13 +1,13 @@ running 2 tests -test $DIR/failed-doctest-output.rs - OtherStruct (line 22) ... FAILED -test $DIR/failed-doctest-output.rs - SomeStruct (line 12) ... FAILED +test $DIR/failed-doctest-output.rs - OtherStruct (line 25) ... FAILED +test $DIR/failed-doctest-output.rs - SomeStruct (line 15) ... FAILED failures: ----- $DIR/failed-doctest-output.rs - OtherStruct (line 22) stdout ---- +---- $DIR/failed-doctest-output.rs - OtherStruct (line 25) stdout ---- error[E0425]: cannot find value `no` in this scope - --> $DIR/failed-doctest-output.rs:23:1 + --> $DIR/failed-doctest-output.rs:26:1 | LL | no | ^^ not found in this scope @@ -16,8 +16,8 @@ error: aborting due to previous error For more information about this error, try `rustc --explain E0425`. Couldn't compile the test. ----- $DIR/failed-doctest-output.rs - SomeStruct (line 12) stdout ---- -Test executable failed (exit code 101). +---- $DIR/failed-doctest-output.rs - SomeStruct (line 15) stdout ---- +Test executable failed (exit status: 101). stdout: stdout 1 @@ -32,8 +32,8 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace failures: - $DIR/failed-doctest-output.rs - OtherStruct (line 22) - $DIR/failed-doctest-output.rs - SomeStruct (line 12) + $DIR/failed-doctest-output.rs - OtherStruct (line 25) + $DIR/failed-doctest-output.rs - SomeStruct (line 15) test result: FAILED. 0 passed; 2 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/test/ui/cast/cast-macro-lhs.rs b/src/test/ui/cast/cast-macro-lhs.rs new file mode 100644 index 00000000000..b509b3239bc --- /dev/null +++ b/src/test/ui/cast/cast-macro-lhs.rs @@ -0,0 +1,12 @@ +// Test to make sure we suggest "consider casting" on the right span + +macro_rules! foo { + () => { 0 } +} + +fn main() { + let x = foo!() as *const [u8]; + //~^ ERROR cannot cast `usize` to a pointer that is wide + //~| NOTE creating a `*const [u8]` requires both an address and a length + //~| NOTE consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` +} diff --git a/src/test/ui/cast/cast-macro-lhs.stderr b/src/test/ui/cast/cast-macro-lhs.stderr new file mode 100644 index 00000000000..db7ce57e150 --- /dev/null +++ b/src/test/ui/cast/cast-macro-lhs.stderr @@ -0,0 +1,11 @@ +error[E0606]: cannot cast `usize` to a pointer that is wide + --> $DIR/cast-macro-lhs.rs:8:23 + | +LL | let x = foo!() as *const [u8]; + | ------ ^^^^^^^^^^^ creating a `*const [u8]` requires both an address and a length + | | + | consider casting this expression to `*const ()`, then using `core::ptr::from_raw_parts` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0606`. diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr index b1141cf3bdf..9f6c7ccf3fe 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.min.stderr @@ -7,12 +7,5 @@ LL | fn test<const T: &'static dyn A>() { = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error[E0741]: `&'static (dyn A + 'static)` must be annotated with `#[derive(PartialEq, Eq)]` to be used as the type of a const parameter - --> $DIR/issue-63322-forbid-dyn.rs:9:18 - | -LL | fn test<const T: &'static dyn A>() { - | ^^^^^^^^^^^^^^ `&'static (dyn A + 'static)` doesn't derive both `PartialEq` and `Eq` - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0741`. diff --git a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs index 01a6caa130f..116c3fcfb21 100644 --- a/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs +++ b/src/test/ui/const-generics/issues/issue-63322-forbid-dyn.rs @@ -7,7 +7,7 @@ struct B; impl A for B {} fn test<const T: &'static dyn A>() { - //~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used + //[full]~^ ERROR must be annotated with `#[derive(PartialEq, Eq)]` to be used //[min]~^^ ERROR `&'static (dyn A + 'static)` is forbidden unimplemented!() } diff --git a/src/test/ui/const-generics/nested-type.min.stderr b/src/test/ui/const-generics/nested-type.min.stderr index 0e3c988ae4d..276ebf31ff8 100644 --- a/src/test/ui/const-generics/nested-type.min.stderr +++ b/src/test/ui/const-generics/nested-type.min.stderr @@ -14,14 +14,5 @@ LL | | }]>; = note: the only supported types are integers, `bool` and `char` = help: more complex types are supported with `#![feature(adt_const_params)]` -error[E0015]: cannot call non-const fn `Foo::{constant#0}::Foo::<17_usize>::value` in constants - --> $DIR/nested-type.rs:15:5 - | -LL | Foo::<17>::value() - | ^^^^^^^^^^^^^^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0015`. diff --git a/src/test/ui/const-generics/nested-type.rs b/src/test/ui/const-generics/nested-type.rs index 5240f5c3b0b..742340f430e 100644 --- a/src/test/ui/const-generics/nested-type.rs +++ b/src/test/ui/const-generics/nested-type.rs @@ -13,7 +13,7 @@ struct Foo<const N: [u8; { //[min]~ ERROR `[u8; _]` is forbidden } Foo::<17>::value() - //~^ ERROR cannot call non-const fn + //[full]~^ ERROR cannot call non-const fn }]>; fn main() {} diff --git a/src/test/ui/lint/unused/unused-attr-duplicate.rs b/src/test/ui/lint/unused/unused-attr-duplicate.rs index 074d5a92ad6..692617eacfb 100644 --- a/src/test/ui/lint/unused/unused-attr-duplicate.rs +++ b/src/test/ui/lint/unused/unused-attr-duplicate.rs @@ -13,9 +13,6 @@ #![crate_name = "unused_attr_duplicate"] #![crate_name = "unused_attr_duplicate2"] //~ ERROR unused attribute //~^ WARN this was previously accepted -#![crate_type = "bin"] -#![crate_type = "rlib"] //~ ERROR unused attribute -//~^ WARN this was previously accepted #![recursion_limit = "128"] #![recursion_limit = "256"] //~ ERROR unused attribute //~^ WARN this was previously accepted diff --git a/src/test/ui/lint/unused/unused-attr-duplicate.stderr b/src/test/ui/lint/unused/unused-attr-duplicate.stderr index d4305add0aa..f592323b550 100644 --- a/src/test/ui/lint/unused/unused-attr-duplicate.stderr +++ b/src/test/ui/lint/unused/unused-attr-duplicate.stderr @@ -1,5 +1,5 @@ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:36:1 + --> $DIR/unused-attr-duplicate.rs:33:1 | LL | #[no_link] | ^^^^^^^^^^ help: remove this attribute @@ -10,180 +10,180 @@ note: the lint level is defined here LL | #![deny(unused_attributes)] | ^^^^^^^^^^^^^^^^^ note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:35:1 + --> $DIR/unused-attr-duplicate.rs:32:1 | LL | #[no_link] | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:40:1 + --> $DIR/unused-attr-duplicate.rs:37:1 | LL | #[macro_use] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:39:1 + --> $DIR/unused-attr-duplicate.rs:36:1 | LL | #[macro_use] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:50:1 + --> $DIR/unused-attr-duplicate.rs:47:1 | LL | #[path = "bar.rs"] | ^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:49:1 + --> $DIR/unused-attr-duplicate.rs:46:1 | LL | #[path = "auxiliary/lint_unused_extern_crate.rs"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:56:1 + --> $DIR/unused-attr-duplicate.rs:53:1 | LL | #[ignore = "some text"] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:55:1 + --> $DIR/unused-attr-duplicate.rs:52:1 | LL | #[ignore] | ^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:58:1 + --> $DIR/unused-attr-duplicate.rs:55:1 | LL | #[should_panic(expected = "values don't match")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:57:1 + --> $DIR/unused-attr-duplicate.rs:54:1 | LL | #[should_panic] | ^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:63:1 + --> $DIR/unused-attr-duplicate.rs:60:1 | LL | #[must_use = "some message"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:62:1 + --> $DIR/unused-attr-duplicate.rs:59:1 | LL | #[must_use] | ^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:69:1 + --> $DIR/unused-attr-duplicate.rs:66:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:68:1 + --> $DIR/unused-attr-duplicate.rs:65:1 | LL | #[non_exhaustive] | ^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:73:1 + --> $DIR/unused-attr-duplicate.rs:70:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:72:1 + --> $DIR/unused-attr-duplicate.rs:69:1 | LL | #[automatically_derived] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:77:1 + --> $DIR/unused-attr-duplicate.rs:74:1 | LL | #[inline(never)] | ^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:76:1 + --> $DIR/unused-attr-duplicate.rs:73:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:80:1 + --> $DIR/unused-attr-duplicate.rs:77:1 | LL | #[cold] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:79:1 + --> $DIR/unused-attr-duplicate.rs:76:1 | LL | #[cold] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:82:1 + --> $DIR/unused-attr-duplicate.rs:79:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:81:1 + --> $DIR/unused-attr-duplicate.rs:78:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:95:1 + --> $DIR/unused-attr-duplicate.rs:92:1 | LL | #[export_name = "exported_symbol_name"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:97:1 + --> $DIR/unused-attr-duplicate.rs:94:1 | LL | #[export_name = "exported_symbol_name2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:101:1 + --> $DIR/unused-attr-duplicate.rs:98:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:100:1 + --> $DIR/unused-attr-duplicate.rs:97:1 | LL | #[no_mangle] | ^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:105:1 + --> $DIR/unused-attr-duplicate.rs:102:1 | LL | #[used] | ^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:104:1 + --> $DIR/unused-attr-duplicate.rs:101:1 | LL | #[used] | ^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:89:5 + --> $DIR/unused-attr-duplicate.rs:86:5 | LL | #[link_name = "this_does_not_exist"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:91:5 + --> $DIR/unused-attr-duplicate.rs:88:5 | LL | #[link_name = "rust_dbg_extern_identity_u32"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -205,102 +205,89 @@ LL | #![crate_name = "unused_attr_duplicate"] error: unused attribute --> $DIR/unused-attr-duplicate.rs:17:1 | -LL | #![crate_type = "rlib"] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute - | -note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:16:1 - | -LL | #![crate_type = "bin"] - | ^^^^^^^^^^^^^^^^^^^^^^ - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - -error: unused attribute - --> $DIR/unused-attr-duplicate.rs:20:1 - | LL | #![recursion_limit = "256"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:19:1 + --> $DIR/unused-attr-duplicate.rs:16:1 | LL | #![recursion_limit = "128"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:23:1 + --> $DIR/unused-attr-duplicate.rs:20:1 | LL | #![type_length_limit = "1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:22:1 + --> $DIR/unused-attr-duplicate.rs:19:1 | LL | #![type_length_limit = "1048576"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:26:1 + --> $DIR/unused-attr-duplicate.rs:23:1 | LL | #![no_std] | ^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:25:1 + --> $DIR/unused-attr-duplicate.rs:22:1 | LL | #![no_std] | ^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:28:1 + --> $DIR/unused-attr-duplicate.rs:25:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:27:1 + --> $DIR/unused-attr-duplicate.rs:24:1 | LL | #![no_implicit_prelude] | ^^^^^^^^^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:30:1 + --> $DIR/unused-attr-duplicate.rs:27:1 | LL | #![windows_subsystem = "windows"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:29:1 + --> $DIR/unused-attr-duplicate.rs:26:1 | LL | #![windows_subsystem = "console"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: unused attribute - --> $DIR/unused-attr-duplicate.rs:33:1 + --> $DIR/unused-attr-duplicate.rs:30:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:32:1 + --> $DIR/unused-attr-duplicate.rs:29:1 | LL | #![no_builtins] | ^^^^^^^^^^^^^^^ error: unused attribute - --> $DIR/unused-attr-duplicate.rs:43:5 + --> $DIR/unused-attr-duplicate.rs:40:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ help: remove this attribute | note: attribute also specified here - --> $DIR/unused-attr-duplicate.rs:42:5 + --> $DIR/unused-attr-duplicate.rs:39:5 | LL | #[macro_export] | ^^^^^^^^^^^^^^^ -error: aborting due to 24 previous errors +error: aborting due to 23 previous errors diff --git a/src/test/ui/privacy/restricted/relative-2018.rs b/src/test/ui/privacy/restricted/relative-2018.rs index 69b7c1e4d4f..954169a9ffb 100644 --- a/src/test/ui/privacy/restricted/relative-2018.rs +++ b/src/test/ui/privacy/restricted/relative-2018.rs @@ -7,7 +7,7 @@ mod m { pub(in ::core) struct S4; //~^ ERROR visibilities can only be restricted to ancestor modules pub(in a::b) struct S5; - //~^ ERROR relative paths are not supported in visibilities on 2018 edition + //~^ ERROR relative paths are not supported in visibilities in 2018 edition or later } fn main() {} diff --git a/src/test/ui/privacy/restricted/relative-2018.stderr b/src/test/ui/privacy/restricted/relative-2018.stderr index 54fee085ee9..dec0d5157da 100644 --- a/src/test/ui/privacy/restricted/relative-2018.stderr +++ b/src/test/ui/privacy/restricted/relative-2018.stderr @@ -4,7 +4,7 @@ error[E0742]: visibilities can only be restricted to ancestor modules LL | pub(in ::core) struct S4; | ^^^^^^ -error: relative paths are not supported in visibilities on 2018 edition +error: relative paths are not supported in visibilities in 2018 edition or later --> $DIR/relative-2018.rs:9:12 | LL | pub(in a::b) struct S5; |
