diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6840b20c89ac..a58d4f2a7c6a4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -142,7 +142,7 @@ the issue in question. Please make sure your pull request is in compliance with Rust's style guidelines by running - $ python x.py test src/tools/tidy + $ python x.py test tidy Make this check before every pull request (and every new commit in a pull request); you can add [git hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md index c501378bff549..87da7327fe619 100644 --- a/src/bootstrap/README.md +++ b/src/bootstrap/README.md @@ -55,6 +55,9 @@ The script accepts commands, flags, and arguments to determine what to do: # run all unit tests ./x.py test + # execute tool tests + ./x.py test tidy + # execute the UI test suite ./x.py test src/test/ui diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index 0b2ab6bbbc021..d8831c6d9e566 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -359,7 +359,7 @@ Arguments: subcommand_help.push_str( "\n Arguments: - This subcommand accepts a number of paths to directories to tests that + This subcommand accepts a number of paths to test directories that should be compiled and run. For example: ./x.py test src/test/ui @@ -372,6 +372,10 @@ Arguments: just like `build src/libstd --stage N` it tests the compiler produced by the previous stage. + Execute tool tests with a tool name argument: + + ./x.py test tidy + If no arguments are passed then the complete artifacts for that stage are compiled and tested. diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index f661b83042868..d1956270f135f 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2626,13 +2626,21 @@ impl Clone for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T> Drop for IntoIter { fn drop(&mut self) { + struct DropGuard<'a, T>(&'a mut IntoIter); + + impl Drop for DropGuard<'_, T> { + fn drop(&mut self) { + // RawVec handles deallocation + let _ = unsafe { RawVec::from_raw_parts(self.0.buf.as_ptr(), self.0.cap) }; + } + } + + let guard = DropGuard(self); // destroy the remaining elements unsafe { - ptr::drop_in_place(self.as_mut_slice()); + ptr::drop_in_place(guard.0.as_mut_slice()); } - - // RawVec handles deallocation - let _ = unsafe { RawVec::from_raw_parts(self.buf.as_ptr(), self.cap) }; + // now `guard` will be dropped and do the rest } } diff --git a/src/libcore/macros/mod.rs b/src/libcore/macros/mod.rs index 76e58f0cc62bc..04af5b5f76828 100644 --- a/src/libcore/macros/mod.rs +++ b/src/libcore/macros/mod.rs @@ -1,3 +1,4 @@ +#[cfg(bootstrap)] #[doc(include = "panic.md")] #[macro_export] #[allow_internal_unstable(core_panic, track_caller)] @@ -20,6 +21,26 @@ macro_rules! panic { ); } +#[cfg(not(bootstrap))] +#[doc(include = "panic.md")] +#[macro_export] +#[allow_internal_unstable(core_panic, track_caller)] +#[stable(feature = "core", since = "1.6.0")] +macro_rules! panic { + () => ( + $crate::panic!("explicit panic") + ); + ($msg:expr) => ( + $crate::panicking::panic($msg) + ); + ($msg:expr,) => ( + $crate::panic!($msg) + ); + ($fmt:expr, $($arg:tt)+) => ( + $crate::panicking::panic_fmt($crate::format_args!($fmt, $($arg)+)) + ); +} + /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// On panic, this macro will print the values of the expressions with their diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 61b764f2d6206..3587f3f0ebf56 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -32,6 +32,7 @@ use crate::fmt; use crate::panic::{Location, PanicInfo}; +/// The underlying implementation of libcore's `panic!` macro when no formatting is used. #[cold] // never inline unless panic_immediate_abort to avoid code // bloat at the call sites as much as possible @@ -49,9 +50,28 @@ pub fn panic(expr: &str) -> ! { // truncation and padding (even though none is used here). Using // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), Location::caller()) + #[cfg(not(bootstrap))] + panic_fmt(fmt::Arguments::new_v1(&[expr], &[])); + #[cfg(bootstrap)] + panic_fmt(fmt::Arguments::new_v1(&[expr], &[]), Location::caller()); } +#[cfg(not(bootstrap))] +#[cold] +#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] +#[track_caller] +#[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access +fn panic_bounds_check(index: usize, len: usize) -> ! { + if cfg!(feature = "panic_immediate_abort") { + unsafe { super::intrinsics::abort() } + } + + panic!("index out of bounds: the len is {} but the index is {}", len, index) +} + +// For bootstrap, we need a variant with the old argument order, and a corresponding +// `panic_fmt`. +#[cfg(bootstrap)] #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access @@ -66,10 +86,12 @@ fn panic_bounds_check(location: &Location<'_>, index: usize, len: usize) -> ! { ) } +/// The underlying implementation of libcore's `panic!` macro when formatting is used. #[cold] #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] -pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! { +#[cfg_attr(not(bootstrap), track_caller)] +pub fn panic_fmt(fmt: fmt::Arguments<'_>, #[cfg(bootstrap)] location: &Location<'_>) -> ! { if cfg!(feature = "panic_immediate_abort") { unsafe { super::intrinsics::abort() } } @@ -81,6 +103,10 @@ pub fn panic_fmt(fmt: fmt::Arguments<'_>, location: &Location<'_>) -> ! { fn panic_impl(pi: &PanicInfo<'_>) -> !; } + #[cfg(bootstrap)] let pi = PanicInfo::internal_constructor(Some(&fmt), location); + #[cfg(not(bootstrap))] + let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller()); + unsafe { panic_impl(&pi) } } diff --git a/src/librustc/mir/interpret/error.rs b/src/librustc/mir/interpret/error.rs index b46095927b709..0b33408edf02d 100644 --- a/src/librustc/mir/interpret/error.rs +++ b/src/librustc/mir/interpret/error.rs @@ -5,15 +5,18 @@ use crate::mir; use crate::mir::interpret::ConstValue; use crate::ty::layout::{Align, LayoutError, Size}; use crate::ty::query::TyCtxtAt; +use crate::ty::tls; use crate::ty::{self, layout, Ty}; use backtrace::Backtrace; +use rustc_data_structures::sync::Lock; use rustc_errors::{struct_span_err, DiagnosticBuilder}; use rustc_hir as hir; use rustc_macros::HashStable; +use rustc_session::CtfeBacktrace; use rustc_span::{Pos, Span}; use rustc_target::spec::abi::Abi; -use std::{any::Any, env, fmt}; +use std::{any::Any, fmt}; #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable, RustcEncodable, RustcDecodable)] pub enum ErrorHandled { @@ -257,21 +260,25 @@ impl From for InterpErrorInfo<'_> { impl<'tcx> From> for InterpErrorInfo<'tcx> { fn from(kind: InterpError<'tcx>) -> Self { - let backtrace = match env::var("RUSTC_CTFE_BACKTRACE") { - // Matching `RUST_BACKTRACE` -- we treat "0" the same as "not present". - Ok(ref val) if val != "0" => { - let mut backtrace = Backtrace::new_unresolved(); + let capture_backtrace = tls::with_context_opt(|ctxt| { + if let Some(ctxt) = ctxt { + *Lock::borrow(&ctxt.tcx.sess.ctfe_backtrace) + } else { + CtfeBacktrace::Disabled + } + }); - if val == "immediate" { - // Print it now. - print_backtrace(&mut backtrace); - None - } else { - Some(Box::new(backtrace)) - } + let backtrace = match capture_backtrace { + CtfeBacktrace::Disabled => None, + CtfeBacktrace::Capture => Some(Box::new(Backtrace::new_unresolved())), + CtfeBacktrace::Immediate => { + // Print it now. + let mut backtrace = Backtrace::new_unresolved(); + print_backtrace(&mut backtrace); + None } - _ => None, }; + InterpErrorInfo { kind, backtrace } } } diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs index b67b7d346f756..3fc6444168e24 100644 --- a/src/librustc_ast/token.rs +++ b/src/librustc_ast/token.rs @@ -535,6 +535,16 @@ impl Token { false } + // Is the token an interpolated block (`$b:block`)? + pub fn is_whole_block(&self) -> bool { + if let Interpolated(ref nt) = self.kind { + if let NtBlock(..) = **nt { + return true; + } + } + false + } + /// Returns `true` if the token is either the `mut` or `const` keyword. pub fn is_mutability(&self) -> bool { self.is_keyword(kw::Mut) || self.is_keyword(kw::Const) diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index 821377db0ee15..4e7bc9fa0e2ae 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs @@ -1,5 +1,3 @@ -use std::ffi::CString; - use crate::attributes; use libc::c_uint; use rustc::bug; @@ -50,8 +48,8 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc args.len() as c_uint, False, ); - let name = CString::new(format!("__rust_{}", method.name)).unwrap(); - let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr(), ty); + let name = format!("__rust_{}", method.name); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); if tcx.sess.target.target.options.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); @@ -60,8 +58,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc attributes::emit_uwtable(llfn, true); } - let callee = CString::new(kind.fn_name(method.name)).unwrap(); - let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr(), ty); + let callee = kind.fn_name(method.name); + let callee = + llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); @@ -73,14 +72,8 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc .enumerate() .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) .collect::>(); - let ret = llvm::LLVMRustBuildCall( - llbuilder, - callee, - args.as_ptr(), - args.len() as c_uint, - None, - "\0".as_ptr().cast(), - ); + let ret = + llvm::LLVMRustBuildCall(llbuilder, callee, args.as_ptr(), args.len() as c_uint, None); llvm::LLVMSetTailCall(ret, True); if output.is_some() { llvm::LLVMBuildRet(llbuilder, ret); diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs index 7975a70ab269c..6edc3d5ecd477 100644 --- a/src/librustc_codegen_llvm/asm.rs +++ b/src/librustc_codegen_llvm/asm.rs @@ -12,7 +12,6 @@ use rustc_span::Span; use libc::{c_char, c_uint}; use log::debug; -use std::ffi::{CStr, CString}; impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { fn codegen_inline_asm( @@ -80,12 +79,11 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { _ => self.type_struct(&output_types, false), }; - let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); - let constraint_cstr = CString::new(all_constraints).unwrap(); + let asm = ia.asm.as_str(); let r = inline_asm_call( self, &asm, - &constraint_cstr, + &all_constraints, &inputs, output_type, ia.volatile, @@ -125,17 +123,17 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { impl AsmMethods for CodegenCx<'ll, 'tcx> { fn codegen_global_asm(&self, ga: &hir::GlobalAsm) { - let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap(); + let asm = ga.asm.as_str(); unsafe { - llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr()); + llvm::LLVMRustAppendModuleInlineAsm(self.llmod, asm.as_ptr().cast(), asm.len()); } } } fn inline_asm_call( bx: &mut Builder<'a, 'll, 'tcx>, - asm: &CStr, - cons: &CStr, + asm: &str, + cons: &str, inputs: &[&'ll Value], output: &'ll llvm::Type, volatile: bool, @@ -157,13 +155,15 @@ fn inline_asm_call( let fty = bx.cx.type_func(&argtys[..], output); unsafe { // Ask LLVM to verify that the constraints are well-formed. - let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr()); + let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len()); debug!("constraint verification result: {:?}", constraints_ok); if constraints_ok { let v = llvm::LLVMRustInlineAsm( fty, - asm.as_ptr(), - cons.as_ptr(), + asm.as_ptr().cast(), + asm.len(), + cons.as_ptr().cast(), + cons.len(), volatile, alignstack, llvm::AsmDialect::from_generic(dia), diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 32604d5601d30..1c5987f26f129 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -1016,7 +1016,6 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { args.as_ptr() as *const &llvm::Value, args.len() as c_uint, bundle, - UNNAMED, ) } } diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index 691f32dd85a05..fab6321186b2c 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -21,7 +21,6 @@ use crate::value::Value; use log::debug; use rustc::ty::Ty; use rustc_codegen_ssa::traits::*; -use rustc_data_structures::small_c_str::SmallCStr; /// Declare a function. /// @@ -34,8 +33,9 @@ fn declare_raw_fn( ty: &'ll Type, ) -> &'ll Value { debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); - let namebuf = SmallCStr::new(name); - let llfn = unsafe { llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty) }; + let llfn = unsafe { + llvm::LLVMRustGetOrInsertFunction(cx.llmod, name.as_ptr().cast(), name.len(), ty) + }; llvm::SetFunctionCallConv(llfn, callconv); // Function addresses in Rust are never significant, allowing functions to @@ -83,8 +83,7 @@ impl DeclareMethods<'tcx> for CodegenCx<'ll, 'tcx> { fn get_declared_value(&self, name: &str) -> Option<&'ll Value> { debug!("get_declared_value(name={:?})", name); - let namebuf = SmallCStr::new(name); - unsafe { llvm::LLVMRustGetNamedValue(self.llmod, namebuf.as_ptr()) } + unsafe { llvm::LLVMRustGetNamedValue(self.llmod, name.as_ptr().cast(), name.len()) } } fn get_defined_value(&self, name: &str) -> Option<&'ll Value> { diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index 31a0f52809088..388b6c7483958 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -732,7 +732,7 @@ extern "C" { /// See Module::setModuleInlineAsm. pub fn LLVMSetModuleInlineAsm(M: &Module, Asm: *const c_char); - pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char); + pub fn LLVMRustAppendModuleInlineAsm(M: &Module, Asm: *const c_char, AsmLen: size_t); /// See llvm::LLVMTypeKind::getTypeID. pub fn LLVMRustGetTypeKind(Ty: &Type) -> TypeKind; @@ -879,13 +879,18 @@ extern "C" { pub fn LLVMSetThreadLocalMode(GlobalVar: &Value, Mode: ThreadLocalMode); pub fn LLVMIsGlobalConstant(GlobalVar: &Value) -> Bool; pub fn LLVMSetGlobalConstant(GlobalVar: &Value, IsConstant: Bool); - pub fn LLVMRustGetNamedValue(M: &Module, Name: *const c_char) -> Option<&Value>; + pub fn LLVMRustGetNamedValue( + M: &Module, + Name: *const c_char, + NameLen: size_t, + ) -> Option<&Value>; pub fn LLVMSetTailCall(CallInst: &Value, IsTailCall: Bool); // Operations on functions pub fn LLVMRustGetOrInsertFunction( M: &'a Module, Name: *const c_char, + NameLen: size_t, FunctionTy: &'a Type, ) -> &'a Value; pub fn LLVMSetFunctionCallConv(Fn: &Value, CC: c_uint); @@ -1332,7 +1337,6 @@ extern "C" { Args: *const &'a Value, NumArgs: c_uint, Bundle: Option<&OperandBundleDef<'a>>, - Name: *const c_char, ) -> &'a Value; pub fn LLVMRustBuildMemCpy( B: &Builder<'a>, @@ -1581,12 +1585,18 @@ extern "C" { pub fn LLVMRustInlineAsm( Ty: &Type, AsmString: *const c_char, + AsmStringLen: size_t, Constraints: *const c_char, + ConstraintsLen: size_t, SideEffects: Bool, AlignStack: Bool, Dialect: AsmDialect, ) -> &Value; - pub fn LLVMRustInlineAsmVerify(Ty: &Type, Constraints: *const c_char) -> bool; + pub fn LLVMRustInlineAsmVerify( + Ty: &Type, + Constraints: *const c_char, + ConstraintsLen: size_t, + ) -> bool; pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustVersionMajor() -> u32; diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index c8d352cd2dd98..9ce23ff5f3fc8 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -415,11 +415,15 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { AssertKind::BoundsCheck { ref len, ref index } => { let len = self.codegen_operand(&mut bx, len).immediate(); let index = self.codegen_operand(&mut bx, index).immediate(); - (lang_items::PanicBoundsCheckFnLangItem, vec![location, index, len]) + // It's `fn panic_bounds_check(index: usize, len: usize)`, + // and `#[track_caller]` adds an implicit third argument. + (lang_items::PanicBoundsCheckFnLangItem, vec![index, len, location]) } _ => { let msg_str = Symbol::intern(msg.description()); let msg = bx.const_str(msg_str); + // It's `pub fn panic(expr: &str)`, with the wide reference being passed + // as two arguments, and `#[track_caller]` adds an implicit third argument. (lang_items::PanicFnLangItem, vec![msg.0, msg.1, location]) } }; diff --git a/src/librustc_error_codes/error_codes.rs b/src/librustc_error_codes/error_codes.rs index 11d1209923fb2..0e14e2196caf0 100644 --- a/src/librustc_error_codes/error_codes.rs +++ b/src/librustc_error_codes/error_codes.rs @@ -411,6 +411,7 @@ E0735: include_str!("./error_codes/E0735.md"), E0736: include_str!("./error_codes/E0736.md"), E0737: include_str!("./error_codes/E0737.md"), E0738: include_str!("./error_codes/E0738.md"), +E0739: include_str!("./error_codes/E0739.md"), E0740: include_str!("./error_codes/E0740.md"), E0741: include_str!("./error_codes/E0741.md"), E0742: include_str!("./error_codes/E0742.md"), @@ -610,5 +611,4 @@ E0748: include_str!("./error_codes/E0748.md"), E0722, // Malformed `#[optimize]` attribute E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions E0726, // non-explicit (not `'_`) elided lifetime in unsupported position - E0739, // invalid track_caller application/syntax } diff --git a/src/librustc_error_codes/error_codes/E0739.md b/src/librustc_error_codes/error_codes/E0739.md new file mode 100644 index 0000000000000..707751066edbc --- /dev/null +++ b/src/librustc_error_codes/error_codes/E0739.md @@ -0,0 +1,13 @@ +`#[track_caller]` can not be applied on struct. + +Erroneous code example: + +```compile_fail,E0739 +#![feature(track_caller)] +#[track_caller] +struct Bar { + a: u8, +} +``` + +[RFC 2091]: https://github.com/rust-lang/rfcs/blob/master/text/2091-inline-semantic.md diff --git a/src/librustc_mir/const_eval/machine.rs b/src/librustc_mir/const_eval/machine.rs index ed8029834680c..bb661d3d2a30a 100644 --- a/src/librustc_mir/const_eval/machine.rs +++ b/src/librustc_mir/const_eval/machine.rs @@ -280,7 +280,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, fn assert_panic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, msg: &AssertMessage<'tcx>, _unwind: Option, ) -> InterpResult<'tcx> { diff --git a/src/librustc_mir/interpret/machine.rs b/src/librustc_mir/interpret/machine.rs index 6615cc608fb54..087517ff4e31d 100644 --- a/src/librustc_mir/interpret/machine.rs +++ b/src/librustc_mir/interpret/machine.rs @@ -165,7 +165,6 @@ pub trait Machine<'mir, 'tcx>: Sized { /// Called to evaluate `Assert` MIR terminators that trigger a panic. fn assert_panic( ecx: &mut InterpCx<'mir, 'tcx, Self>, - span: Span, msg: &mir::AssertMessage<'tcx>, unwind: Option, ) -> InterpResult<'tcx>; diff --git a/src/librustc_mir/interpret/terminator.rs b/src/librustc_mir/interpret/terminator.rs index ea8378574a3e0..b5c34daf8a318 100644 --- a/src/librustc_mir/interpret/terminator.rs +++ b/src/librustc_mir/interpret/terminator.rs @@ -95,7 +95,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if expected == cond_val { self.go_to_block(target); } else { - M::assert_panic(self, terminator.source_info.span, msg, cleanup)?; + M::assert_panic(self, msg, cleanup)?; } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index a07c8575b300c..f9682a77173f5 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -197,7 +197,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine { fn assert_panic( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _span: Span, _msg: &rustc::mir::AssertMessage<'tcx>, _unwind: Option, ) -> InterpResult<'tcx> { diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 10d524776a11b..fe75062ee503c 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -2,6 +2,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(bindings_after_at)] use rustc_ast::ast; use rustc_ast::token::{self, Nonterminal}; diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs index bdd78e671a8b3..b56dd30739dae 100644 --- a/src/librustc_parse/parser/attr.rs +++ b/src/librustc_parse/parser/attr.rs @@ -1,4 +1,4 @@ -use super::{Parser, PathStyle, TokenType}; +use super::{Parser, PathStyle}; use rustc_ast::ast; use rustc_ast::attr; use rustc_ast::token::{self, Nonterminal}; @@ -10,14 +10,20 @@ use rustc_span::{Span, Symbol}; use log::debug; #[derive(Debug)] -enum InnerAttributeParsePolicy<'a> { +pub(super) enum InnerAttrPolicy<'a> { Permitted, - NotPermitted { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option }, + Forbidden { reason: &'a str, saw_doc_comment: bool, prev_attr_sp: Option }, } const DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG: &str = "an inner attribute is not \ permitted in this context"; +pub(super) const DEFAULT_INNER_ATTR_FORBIDDEN: InnerAttrPolicy<'_> = InnerAttrPolicy::Forbidden { + reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, + saw_doc_comment: false, + prev_attr_sp: None, +}; + impl<'a> Parser<'a> { /// Parses attributes that appear before an item. pub(super) fn parse_outer_attributes(&mut self) -> PResult<'a, Vec> { @@ -25,48 +31,44 @@ impl<'a> Parser<'a> { let mut just_parsed_doc_comment = false; loop { debug!("parse_outer_attributes: self.token={:?}", self.token); - match self.token.kind { - token::Pound => { - let inner_error_reason = if just_parsed_doc_comment { - "an inner attribute is not permitted following an outer doc comment" - } else if !attrs.is_empty() { - "an inner attribute is not permitted following an outer attribute" - } else { - DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG - }; - let inner_parse_policy = InnerAttributeParsePolicy::NotPermitted { - reason: inner_error_reason, - saw_doc_comment: just_parsed_doc_comment, - prev_attr_sp: attrs.last().map(|a| a.span), - }; - let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; - attrs.push(attr); - just_parsed_doc_comment = false; - } - token::DocComment(s) => { - let attr = self.mk_doc_comment(s); - if attr.style != ast::AttrStyle::Outer { - let span = self.token.span; - let mut err = self.struct_span_err(span, "expected outer doc comment"); - err.note( + if self.check(&token::Pound) { + let inner_error_reason = if just_parsed_doc_comment { + "an inner attribute is not permitted following an outer doc comment" + } else if !attrs.is_empty() { + "an inner attribute is not permitted following an outer attribute" + } else { + DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG + }; + let inner_parse_policy = InnerAttrPolicy::Forbidden { + reason: inner_error_reason, + saw_doc_comment: just_parsed_doc_comment, + prev_attr_sp: attrs.last().map(|a| a.span), + }; + let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?; + attrs.push(attr); + just_parsed_doc_comment = false; + } else if let token::DocComment(s) = self.token.kind { + let attr = self.mk_doc_comment(s); + if attr.style != ast::AttrStyle::Outer { + self.struct_span_err(self.token.span, "expected outer doc comment") + .note( "inner doc comments like this (starting with \ - `//!` or `/*!`) can only appear before items", - ); - return Err(err); - } - attrs.push(attr); - self.bump(); - just_parsed_doc_comment = true; + `//!` or `/*!`) can only appear before items", + ) + .emit(); } - _ => break, + attrs.push(attr); + self.bump(); + just_parsed_doc_comment = true; + } else { + break; } } Ok(attrs) } fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute { - let style = comments::doc_comment_style(&s.as_str()); - attr::mk_doc_comment(style, s, self.token.span) + attr::mk_doc_comment(comments::doc_comment_style(&s.as_str()), s, self.token.span) } /// Matches `attribute = # ! [ meta_item ]`. @@ -75,96 +77,67 @@ impl<'a> Parser<'a> { /// attribute. pub fn parse_attribute(&mut self, permit_inner: bool) -> PResult<'a, ast::Attribute> { debug!("parse_attribute: permit_inner={:?} self.token={:?}", permit_inner, self.token); - let inner_parse_policy = if permit_inner { - InnerAttributeParsePolicy::Permitted - } else { - InnerAttributeParsePolicy::NotPermitted { - reason: DEFAULT_UNEXPECTED_INNER_ATTR_ERR_MSG, - saw_doc_comment: false, - prev_attr_sp: None, - } - }; + let inner_parse_policy = + if permit_inner { InnerAttrPolicy::Permitted } else { DEFAULT_INNER_ATTR_FORBIDDEN }; self.parse_attribute_with_inner_parse_policy(inner_parse_policy) } - /// The same as `parse_attribute`, except it takes in an `InnerAttributeParsePolicy` + /// The same as `parse_attribute`, except it takes in an `InnerAttrPolicy` /// that prescribes how to handle inner attributes. fn parse_attribute_with_inner_parse_policy( &mut self, - inner_parse_policy: InnerAttributeParsePolicy<'_>, + inner_parse_policy: InnerAttrPolicy<'_>, ) -> PResult<'a, ast::Attribute> { debug!( "parse_attribute_with_inner_parse_policy: inner_parse_policy={:?} self.token={:?}", inner_parse_policy, self.token ); - let (span, item, style) = match self.token.kind { - token::Pound => { - let lo = self.token.span; - self.bump(); - - if let InnerAttributeParsePolicy::Permitted = inner_parse_policy { - self.expected_tokens.push(TokenType::Token(token::Not)); - } - - let style = if self.token == token::Not { - self.bump(); - ast::AttrStyle::Inner - } else { - ast::AttrStyle::Outer - }; + let lo = self.token.span; + let (span, item, style) = if self.eat(&token::Pound) { + let style = + if self.eat(&token::Not) { ast::AttrStyle::Inner } else { ast::AttrStyle::Outer }; - self.expect(&token::OpenDelim(token::Bracket))?; - let item = self.parse_attr_item()?; - self.expect(&token::CloseDelim(token::Bracket))?; - let hi = self.prev_token.span; - - let attr_sp = lo.to(hi); - - // Emit error if inner attribute is encountered and not permitted - if style == ast::AttrStyle::Inner { - if let InnerAttributeParsePolicy::NotPermitted { - reason, - saw_doc_comment, - prev_attr_sp, - } = inner_parse_policy - { - let prev_attr_note = if saw_doc_comment { - "previous doc comment" - } else { - "previous outer attribute" - }; - - let mut diagnostic = self.struct_span_err(attr_sp, reason); - - if let Some(prev_attr_sp) = prev_attr_sp { - diagnostic - .span_label(attr_sp, "not permitted following an outer attribute") - .span_label(prev_attr_sp, prev_attr_note); - } - - diagnostic - .note( - "inner attributes, like `#![no_std]`, annotate the item \ - enclosing them, and are usually found at the beginning of \ - source files. Outer attributes, like `#[test]`, annotate the \ - item following them.", - ) - .emit(); - } - } + self.expect(&token::OpenDelim(token::Bracket))?; + let item = self.parse_attr_item()?; + self.expect(&token::CloseDelim(token::Bracket))?; + let attr_sp = lo.to(self.prev_token.span); - (attr_sp, item, style) - } - _ => { - let token_str = pprust::token_to_string(&self.token); - let msg = &format!("expected `#`, found `{}`", token_str); - return Err(self.struct_span_err(self.token.span, msg)); + // Emit error if inner attribute is encountered and forbidden. + if style == ast::AttrStyle::Inner { + self.error_on_forbidden_inner_attr(attr_sp, inner_parse_policy); } + + (attr_sp, item, style) + } else { + let token_str = pprust::token_to_string(&self.token); + let msg = &format!("expected `#`, found `{}`", token_str); + return Err(self.struct_span_err(self.token.span, msg)); }; Ok(attr::mk_attr_from_item(style, item, span)) } + pub(super) fn error_on_forbidden_inner_attr(&self, attr_sp: Span, policy: InnerAttrPolicy<'_>) { + if let InnerAttrPolicy::Forbidden { reason, saw_doc_comment, prev_attr_sp } = policy { + let prev_attr_note = + if saw_doc_comment { "previous doc comment" } else { "previous outer attribute" }; + + let mut diag = self.struct_span_err(attr_sp, reason); + + if let Some(prev_attr_sp) = prev_attr_sp { + diag.span_label(attr_sp, "not permitted following an outer attribute") + .span_label(prev_attr_sp, prev_attr_note); + } + + diag.note( + "inner attributes, like `#![no_std]`, annotate the item enclosing them, \ + and are usually found at the beginning of source files. \ + Outer attributes, like `#[test]`, annotate the item following them.", + ) + .emit(); + } + } + /// Parses an inner part of an attribute (the path and following tokens). /// The tokens must be either a delimited token stream, or empty token stream, /// or the "legacy" key-value form. @@ -200,28 +173,22 @@ impl<'a> Parser<'a> { crate fn parse_inner_attributes(&mut self) -> PResult<'a, Vec> { let mut attrs: Vec = vec![]; loop { - match self.token.kind { - token::Pound => { - // Don't even try to parse if it's not an inner attribute. - if !self.look_ahead(1, |t| t == &token::Not) { - break; - } - - let attr = self.parse_attribute(true)?; - assert_eq!(attr.style, ast::AttrStyle::Inner); + // Only try to parse if it is an inner attribute (has `!`). + if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) { + let attr = self.parse_attribute(true)?; + assert_eq!(attr.style, ast::AttrStyle::Inner); + attrs.push(attr); + } else if let token::DocComment(s) = self.token.kind { + // We need to get the position of this token before we bump. + let attr = self.mk_doc_comment(s); + if attr.style == ast::AttrStyle::Inner { attrs.push(attr); + self.bump(); + } else { + break; } - token::DocComment(s) => { - // We need to get the position of this token before we bump. - let attr = self.mk_doc_comment(s); - if attr.style == ast::AttrStyle::Inner { - attrs.push(attr); - self.bump(); - } else { - break; - } - } - _ => break, + } else { + break; } } Ok(attrs) @@ -232,12 +199,10 @@ impl<'a> Parser<'a> { debug!("checking if {:?} is unusuffixed", lit); if !lit.kind.is_unsuffixed() { - let msg = "suffixed literals are not allowed in attributes"; - self.struct_span_err(lit.span, msg) + self.struct_span_err(lit.span, "suffixed literals are not allowed in attributes") .help( - "instead of using a suffixed literal \ - (`1u8`, `1.0f32`, etc.), use an unsuffixed version \ - (`1`, `1.0`, etc.)", + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), \ + use an unsuffixed version (`1`, `1.0`, etc.)", ) .emit(); } diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 16ea2773b2009..7f6f90431fc94 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -673,10 +673,28 @@ impl<'a> Parser<'a> { /// Parse `& mut? ` or `& raw [ const | mut ] `. fn parse_borrow_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> { self.expect_and()?; + let has_lifetime = self.token.is_lifetime() && self.look_ahead(1, |t| t != &token::Colon); + let lifetime = has_lifetime.then(|| self.expect_lifetime()); // For recovery, see below. let (borrow_kind, mutbl) = self.parse_borrow_modifiers(lo); let expr = self.parse_prefix_expr(None); - let (span, expr) = self.interpolated_or_expr_span(expr)?; - Ok((lo.to(span), ExprKind::AddrOf(borrow_kind, mutbl, expr))) + let (hi, expr) = self.interpolated_or_expr_span(expr)?; + let span = lo.to(hi); + if let Some(lt) = lifetime { + self.error_remove_borrow_lifetime(span, lt.ident.span); + } + Ok((span, ExprKind::AddrOf(borrow_kind, mutbl, expr))) + } + + fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) { + self.struct_span_err(span, "borrow expressions cannot be annotated with lifetimes") + .span_label(lt_span, "annotated with lifetime here") + .span_suggestion( + lt_span, + "remove the lifetime annotation", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); } /// Parse `mut?` or `raw [ const | mut ]`. @@ -901,7 +919,7 @@ impl<'a> Parser<'a> { } else if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; Ok(self.mk_expr(lo.to(path.span), ExprKind::Path(Some(qself), path), attrs)) - } else if self.token.is_path_start() { + } else if self.check_path() { self.parse_path_start_expr(attrs) } else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) { self.parse_closure_expr(attrs) @@ -1067,26 +1085,44 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr, true) } + /// Parse `'label: $expr`. The label is already parsed. fn parse_labeled_expr(&mut self, label: Label, attrs: AttrVec) -> PResult<'a, P> { let lo = label.ident.span; - self.expect(&token::Colon)?; - if self.eat_keyword(kw::While) { - return self.parse_while_expr(Some(label), lo, attrs); - } - if self.eat_keyword(kw::For) { - return self.parse_for_expr(Some(label), lo, attrs); - } - if self.eat_keyword(kw::Loop) { - return self.parse_loop_expr(Some(label), lo, attrs); - } - if self.token == token::OpenDelim(token::Brace) { - return self.parse_block_expr(Some(label), lo, BlockCheckMode::Default, attrs); + let label = Some(label); + let ate_colon = self.eat(&token::Colon); + let expr = if self.eat_keyword(kw::While) { + self.parse_while_expr(label, lo, attrs) + } else if self.eat_keyword(kw::For) { + self.parse_for_expr(label, lo, attrs) + } else if self.eat_keyword(kw::Loop) { + self.parse_loop_expr(label, lo, attrs) + } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() { + self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs) + } else { + let msg = "expected `while`, `for`, `loop` or `{` after a label"; + self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); + // Continue as an expression in an effort to recover on `'label: non_block_expr`. + self.parse_expr() + }?; + + if !ate_colon { + self.error_labeled_expr_must_be_followed_by_colon(lo, expr.span); } - let msg = "expected `while`, `for`, `loop` or `{` after a label"; - self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit(); - // Continue as an expression in an effort to recover on `'label: non_block_expr`. - self.parse_expr() + Ok(expr) + } + + fn error_labeled_expr_must_be_followed_by_colon(&self, lo: Span, span: Span) { + self.struct_span_err(span, "labeled expression must be followed by `:`") + .span_label(lo, "the label") + .span_suggestion_short( + lo.shrink_to_hi(), + "add `:` after the label", + ": ".to_string(), + Applicability::MachineApplicable, + ) + .note("labels are used before loops and blocks, allowing e.g., `break 'label` to them") + .emit(); } /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. @@ -1363,18 +1399,20 @@ impl<'a> Parser<'a> { opt_label: Option