diff --git a/Cargo.lock b/Cargo.lock index 1679cf320182a..7ed732e41605c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,9 +576,9 @@ dependencies = [ [[package]] name = "compiler_builtins" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9975aefa63997ef75ca9cf013ff1bb81487aaa0b622c21053afd3b92979a7af" +checksum = "438ac08ddc5efe81452f984a9e33ba425b00b31d1f48e6acd9e2210aa28cc52e" dependencies = [ "cc", "rustc-std-workspace-core", diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs index 76a6ffbb1c5b2..e3d69fc5c76df 100644 --- a/src/librustc_codegen_llvm/back/lto.rs +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -584,6 +584,20 @@ pub(crate) fn run_pass_manager( // tools/lto/LTOCodeGenerator.cpp debug!("running the pass manager"); unsafe { + if write::should_use_new_llvm_pass_manager(config) { + let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO }; + let opt_level = config.opt_level.unwrap_or(config::OptLevel::No); + // See comment below for why this is necessary. + let opt_level = if let config::OptLevel::No = opt_level { + config::OptLevel::Less + } else { + opt_level + }; + write::optimize_with_new_llvm_pass_manager(module, config, opt_level, opt_stage); + debug!("lto done"); + return; + } + let pm = llvm::LLVMCreatePassManager(); llvm::LLVMAddAnalysisPasses(module.module_llvm.tm, pm); diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs index 7dd57da90c3d6..9008970847a59 100644 --- a/src/librustc_codegen_llvm/back/write.rs +++ b/src/librustc_codegen_llvm/back/write.rs @@ -111,6 +111,18 @@ pub fn to_llvm_opt_settings( } } +fn to_pass_builder_opt_level(cfg: config::OptLevel) -> llvm::PassBuilderOptLevel { + use config::OptLevel::*; + match cfg { + No => llvm::PassBuilderOptLevel::O0, + Less => llvm::PassBuilderOptLevel::O1, + Default => llvm::PassBuilderOptLevel::O2, + Aggressive => llvm::PassBuilderOptLevel::O3, + Size => llvm::PassBuilderOptLevel::Os, + SizeMin => llvm::PassBuilderOptLevel::Oz, + } +} + // If find_features is true this won't access `sess.crate_types` by assuming // that `is_pie_binary` is false. When we discover LLVM target features // `sess.crate_types` is uninitialized so we cannot access it. @@ -303,6 +315,88 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void } } +fn get_pgo_gen_path(config: &ModuleConfig) -> Option { + match config.pgo_gen { + SwitchWithOptPath::Enabled(ref opt_dir_path) => { + let path = if let Some(dir_path) = opt_dir_path { + dir_path.join("default_%m.profraw") + } else { + PathBuf::from("default_%m.profraw") + }; + + Some(CString::new(format!("{}", path.display())).unwrap()) + } + SwitchWithOptPath::Disabled => None, + } +} + +fn get_pgo_use_path(config: &ModuleConfig) -> Option { + config + .pgo_use + .as_ref() + .map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap()) +} + +pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool { + // We only support the new pass manager starting with LLVM 9. + if llvm_util::get_major_version() < 9 { + return false; + } + + // The new pass manager is disabled by default. + config.new_llvm_pass_manager.unwrap_or(false) +} + +pub(crate) unsafe fn optimize_with_new_llvm_pass_manager( + module: &ModuleCodegen, + config: &ModuleConfig, + opt_level: config::OptLevel, + opt_stage: llvm::OptStage, +) { + let unroll_loops = + opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin; + let using_thin_buffers = opt_stage == llvm::OptStage::PreLinkThinLTO || config.bitcode_needed(); + let pgo_gen_path = get_pgo_gen_path(config); + let pgo_use_path = get_pgo_use_path(config); + let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO; + // Sanitizer instrumentation is only inserted during the pre-link optimization stage. + let sanitizer_options = if !is_lto { + config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions { + sanitize_memory: *s == Sanitizer::Memory, + sanitize_thread: *s == Sanitizer::Thread, + sanitize_address: *s == Sanitizer::Address, + sanitize_recover: config.sanitizer_recover.contains(s), + sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int, + }) + } else { + None + }; + + // FIXME: NewPM doesn't provide a facility to pass custom InlineParams. + // We would have to add upstream support for this first, before we can support + // config.inline_threshold and our more aggressive default thresholds. + // FIXME: NewPM uses an different and more explicit way to textually represent + // pass pipelines. It would probably make sense to expose this, but it would + // require a different format than the current -C passes. + llvm::LLVMRustOptimizeWithNewPassManager( + module.module_llvm.llmod(), + &*module.module_llvm.tm, + to_pass_builder_opt_level(opt_level), + opt_stage, + config.no_prepopulate_passes, + config.verify_llvm_ir, + using_thin_buffers, + config.merge_functions, + unroll_loops, + config.vectorize_slp, + config.vectorize_loop, + config.no_builtins, + sanitizer_options.as_ref(), + pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()), + ); +} + // Unsafe due to LLVM calls. pub(crate) unsafe fn optimize( cgcx: &CodegenContext, @@ -327,6 +421,17 @@ pub(crate) unsafe fn optimize( } if let Some(opt_level) = config.opt_level { + if should_use_new_llvm_pass_manager(config) { + let opt_stage = match cgcx.lto { + Lto::Fat => llvm::OptStage::PreLinkFatLTO, + Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO, + _ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO, + _ => llvm::OptStage::PreLinkNoLTO, + }; + optimize_with_new_llvm_pass_manager(module, config, opt_level, opt_stage); + return Ok(()); + } + // Create the two optimizing pass managers. These mirror what clang // does, and are by populated by LLVM's default PassManagerBuilder. // Each manager has a different set of passes, but they also share @@ -757,24 +862,8 @@ pub unsafe fn with_llvm_pmb( let opt_size = config.opt_size.map(|x| to_llvm_opt_settings(x).1).unwrap_or(llvm::CodeGenOptSizeNone); let inline_threshold = config.inline_threshold; - - let pgo_gen_path = match config.pgo_gen { - SwitchWithOptPath::Enabled(ref opt_dir_path) => { - let path = if let Some(dir_path) = opt_dir_path { - dir_path.join("default_%m.profraw") - } else { - PathBuf::from("default_%m.profraw") - }; - - Some(CString::new(format!("{}", path.display())).unwrap()) - } - SwitchWithOptPath::Disabled => None, - }; - - let pgo_use_path = config - .pgo_use - .as_ref() - .map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap()); + let pgo_gen_path = get_pgo_gen_path(config); + let pgo_use_path = get_pgo_use_path(config); llvm::LLVMRustConfigurePassManagerBuilder( builder, diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs index 342ac437d3c24..ab6c55becdb02 100644 --- a/src/librustc_codegen_llvm/builder.rs +++ b/src/librustc_codegen_llvm/builder.rs @@ -781,13 +781,18 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { align: Align, flags: MemFlags, ) { - let ptr_width = &self.sess().target.target.target_pointer_width; - let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); - let llintrinsicfn = self.get_intrinsic(&intrinsic_key); + let is_volatile = flags.contains(MemFlags::VOLATILE); let ptr = self.pointercast(ptr, self.type_i8p()); - let align = self.const_u32(align.bytes() as u32); - let volatile = self.const_bool(flags.contains(MemFlags::VOLATILE)); - self.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None); + unsafe { + llvm::LLVMRustBuildMemSet( + self.llbuilder, + ptr, + align.bytes() as c_uint, + fill_byte, + size, + is_volatile, + ); + } } fn select( @@ -985,11 +990,11 @@ impl BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } fn lifetime_start(&mut self, ptr: &'ll Value, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); + self.call_lifetime_intrinsic("llvm.lifetime.start.p0i8", ptr, size); } fn lifetime_end(&mut self, ptr: &'ll Value, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); + self.call_lifetime_intrinsic("llvm.lifetime.end.p0i8", ptr, size); } fn call( diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs index 6b31f14410d2f..46f461b98c8de 100644 --- a/src/librustc_codegen_llvm/context.rs +++ b/src/librustc_codegen_llvm/context.rs @@ -562,10 +562,6 @@ impl CodegenCx<'b, 'tcx> { t_v8f64: t_f64, 8; } - ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); - ifn!("llvm.trap", fn() -> void); ifn!("llvm.debugtrap", fn() -> void); ifn!("llvm.frameaddress", fn(t_i32) -> i8p); @@ -830,8 +826,8 @@ impl CodegenCx<'b, 'tcx> { ifn!("llvm.usub.sat.i64", fn(t_i64, t_i64) -> t_i64); ifn!("llvm.usub.sat.i128", fn(t_i128, t_i128) -> t_i128); - ifn!("llvm.lifetime.start", fn(t_i64, i8p) -> void); - ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); + ifn!("llvm.lifetime.start.p0i8", fn(t_i64, i8p) -> void); + ifn!("llvm.lifetime.end.p0i8", fn(t_i64, i8p) -> void); ifn!("llvm.expect.i1", fn(i1, i1) -> i1); ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs index fca4b1c598c37..f12bfe0e80ace 100644 --- a/src/librustc_codegen_llvm/llvm/ffi.rs +++ b/src/librustc_codegen_llvm/llvm/ffi.rs @@ -402,6 +402,38 @@ pub enum CodeGenOptLevel { Aggressive, } +/// LLVMRustPassBuilderOptLevel +#[repr(C)] +pub enum PassBuilderOptLevel { + O0, + O1, + O2, + O3, + Os, + Oz, +} + +/// LLVMRustOptStage +#[derive(PartialEq)] +#[repr(C)] +pub enum OptStage { + PreLinkNoLTO, + PreLinkThinLTO, + PreLinkFatLTO, + ThinLTO, + FatLTO, +} + +/// LLVMRustSanitizerOptions +#[repr(C)] +pub struct SanitizerOptions { + pub sanitize_memory: bool, + pub sanitize_thread: bool, + pub sanitize_address: bool, + pub sanitize_recover: bool, + pub sanitize_memory_track_origins: c_int, +} + /// LLVMRelocMode #[derive(Copy, Clone, PartialEq)] #[repr(C)] @@ -1316,6 +1348,14 @@ extern "C" { Size: &'a Value, IsVolatile: bool, ) -> &'a Value; + pub fn LLVMRustBuildMemSet( + B: &Builder<'a>, + Dst: &'a Value, + DstAlign: c_uint, + Val: &'a Value, + Size: &'a Value, + IsVolatile: bool, + ) -> &'a Value; pub fn LLVMBuildSelect( B: &Builder<'a>, If: &'a Value, @@ -1889,6 +1929,23 @@ extern "C" { Output: *const c_char, FileType: FileType, ) -> LLVMRustResult; + pub fn LLVMRustOptimizeWithNewPassManager( + M: &'a Module, + TM: &'a TargetMachine, + OptLevel: PassBuilderOptLevel, + OptStage: OptStage, + NoPrepopulatePasses: bool, + VerifyIR: bool, + UseThinLTOBuffers: bool, + MergeFunctions: bool, + UnrollLoops: bool, + SLPVectorize: bool, + LoopVectorize: bool, + DisableSimplifyLibCalls: bool, + SanitizerOptions: Option<&SanitizerOptions>, + PGOGenPath: *const c_char, + PGOUsePath: *const c_char, + ); pub fn LLVMRustPrintModule( M: &'a Module, Output: *const c_char, diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs index 9905b3a56c0fa..92f795acc5438 100644 --- a/src/librustc_codegen_ssa/back/write.rs +++ b/src/librustc_codegen_ssa/back/write.rs @@ -88,6 +88,7 @@ pub struct ModuleConfig { pub vectorize_slp: bool, pub merge_functions: bool, pub inline_threshold: Option, + pub new_llvm_pass_manager: Option, // Instead of creating an object file by doing LLVM codegen, just // make the object file bitcode. Provides easy compatibility with // emscripten's ecc compiler, when used as the linker. @@ -132,6 +133,7 @@ impl ModuleConfig { vectorize_slp: false, merge_functions: false, inline_threshold: None, + new_llvm_pass_manager: None, } } @@ -140,6 +142,7 @@ impl ModuleConfig { self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; self.no_builtins = no_builtins || sess.target.target.options.no_builtins; self.inline_threshold = sess.opts.cg.inline_threshold; + self.new_llvm_pass_manager = sess.opts.debugging_opts.new_llvm_pass_manager; self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled(); let embed_bitcode = diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 5050fe4906474..2e8c94903ca46 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -134,9 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let mut r = r as u32; let size = left_layout.size; oflo |= r >= size.bits() as u32; - if oflo { - r %= size.bits() as u32; - } + r %= size.bits() as u32; let result = if signed { let l = self.sign_extend(l, left_layout) as i128; let result = match bin_op { @@ -168,6 +166,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } + let size = left_layout.size; + // Operations that need special treatment for signed integers if left_layout.abi.is_signed() { let op: Option bool> = match bin_op { @@ -195,32 +195,31 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(op) = op { let l128 = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; - let size = left_layout.size; + // We need a special check for overflowing remainder: + // "int_min % -1" overflows and returns 0, but after casting things to a larger int + // type it does *not* overflow nor give an unrepresentable result! match bin_op { - Rem | Div => { - // int_min / -1 + Rem => { if r == -1 && l == (1 << (size.bits() - 1)) { - return Ok((Scalar::from_uint(l, size), true, left_layout.ty)); + return Ok((Scalar::from_int(0, size), true, left_layout.ty)); } } _ => {} } - trace!("{}, {}, {}", l, l128, r); - let (result, mut oflo) = op(l128, r); - trace!("{}, {}", result, oflo); - if !oflo && size.bits() != 128 { - let max = 1 << (size.bits() - 1); - oflo = result >= max || result < -max; - } - // this may be out-of-bounds for the result type, so we have to truncate ourselves + + let (result, oflo) = op(l128, r); + // This may be out-of-bounds for the result type, so we have to truncate ourselves. + // If that truncation loses any information, we have an overflow. let result = result as u128; let truncated = self.truncate(result, left_layout); - return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty)); + return Ok(( + Scalar::from_uint(truncated, size), + oflo || self.sign_extend(truncated, left_layout) != result, + left_layout.ty, + )); } } - let size = left_layout.size; - let (val, ty) = match bin_op { Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), @@ -247,6 +246,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => bug!(), }; let (result, oflo) = op(l, r); + // Truncate to target type. + // If that truncation loses any information, we have an overflow. let truncated = self.truncate(result, left_layout); return Ok(( Scalar::from_uint(truncated, size), @@ -341,7 +342,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - /// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows. + /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows. #[inline] pub fn binary_op( &self, @@ -353,11 +354,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } - pub fn unary_op( + /// Returns the result of the specified operation, whether it overflowed, and + /// the result type. + pub fn overflowing_unary_op( &self, un_op: mir::UnOp, val: ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { use rustc::mir::UnOp::*; let layout = val.layout; @@ -371,7 +374,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Not => !val, _ => bug!("Invalid bool op {:?}", un_op), }; - Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?)) + Ok((Scalar::from_bool(res), false, self.tcx.types.bool)) } ty::Float(fty) => { let res = match (un_op, fty) { @@ -379,21 +382,36 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), _ => bug!("Invalid float op {:?}", un_op), }; - Ok(ImmTy::from_scalar(res, layout)) + Ok((res, false, layout.ty)) } _ => { assert!(layout.ty.is_integral()); let val = self.force_bits(val, layout.size)?; - let res = match un_op { - Not => !val, + let (res, overflow) = match un_op { + Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate Neg => { + // arithmetic negation assert!(layout.abi.is_signed()); - (-(val as i128)) as u128 + let val = self.sign_extend(val, layout) as i128; + let (res, overflow) = val.overflowing_neg(); + let res = res as u128; + // Truncate to target type. + // If that truncation loses any information, we have an overflow. + let truncated = self.truncate(res, layout); + (truncated, overflow || self.sign_extend(truncated, layout) != res) } }; - // res needs tuncating - Ok(ImmTy::from_uint(self.truncate(res, layout), layout)) + Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty)) } } } + + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: ImmTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; + Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) + } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 14c0db2def285..a7da4f7c164fe 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -518,18 +518,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> { + fn check_unary_op( + &mut self, + op: UnOp, + arg: &Operand<'tcx>, + source_info: SourceInfo, + ) -> Option<()> { self.use_ecx(source_info, |this| { - let ty = arg.ty(&this.local_decls, this.tcx); - - if ty.is_integral() { - let arg = this.ecx.eval_operand(arg, None)?; - let prim = this.ecx.read_immediate(arg)?; - // Need to do overflow check here: For actual CTFE, MIR - // generation emits code that does this before calling the op. - if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { - throw_panic!(OverflowNeg) - } + let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?; + let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?; + + if overflow { + assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); + throw_panic!(OverflowNeg); } Ok(()) @@ -576,11 +577,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if !overflow_check { self.use_ecx(source_info, |this| { let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; - let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; if overflow { - let err = err_panic!(Overflow(op)).into(); - return Err(err); + throw_panic!(Overflow(op)); } Ok(()) @@ -620,9 +620,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Additional checking: if overflow checks are disabled (which is usually the case in // release mode), then we need to do additional checking here to give lints to the user // if an overflow would occur. - Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => { - trace!("checking UnaryOp(op = Neg, arg = {:?})", arg); - self.check_unary_op(arg, source_info)?; + Rvalue::UnaryOp(op, arg) if !overflow_check => { + trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); + self.check_unary_op(*op, arg, source_info)?; } // Additional checking: check for overflows on integer binary operations and report diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index c8c0ba4c66ee2..77748d16653a7 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -823,7 +823,7 @@ impl<'a> Parser<'a> { if let Some(args) = segment.args { self.struct_span_err( args.span(), - "field expressions may not have generic arguments", + "field expressions cannot have generic arguments", ) .emit(); } diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs index 4b3645cce723a..a794670d7b8fe 100644 --- a/src/librustc_session/options.rs +++ b/src/librustc_session/options.rs @@ -968,4 +968,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "compile without linking"), link_only: bool = (false, parse_bool, [TRACKED], "link the `.rlink` file generated by `-Z no-link`"), + new_llvm_pass_manager: Option = (None, parse_opt_bool, [TRACKED], + "use new LLVM pass manager"), } diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 9ce89bd636304..90b7b300da9d5 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -1586,7 +1586,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &format!("a method `{}` also exists, call it with parentheses", field), field, expr_t, - expr.hir_id, + expr, ); } err.emit(); @@ -1609,7 +1609,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "use parentheses to call the method", field, expr_t, - expr.hir_id, + expr, ); } else { err.help("methods are immutable and cannot be assigned to"); diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs index 67d8030a9d69b..c3e15c507b30d 100644 --- a/src/librustc_typeck/check/method/mod.rs +++ b/src/librustc_typeck/check/method/mod.rs @@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { msg: &str, method_name: ast::Ident, self_ty: Ty<'tcx>, - call_expr_id: hir::HirId, + call_expr: &hir::Expr<'_>, ) { let has_params = self .probe_for_name( @@ -144,7 +144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { method_name, IsSuggestion(false), self_ty, - call_expr_id, + call_expr.hir_id, ProbeScope::TraitsInScope, ) .and_then(|pick| { @@ -152,13 +152,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ok(sig.inputs().skip_binder().len() > 1) }); + // Account for `foo.bar`; + let sugg_span = method_name.span.with_hi(call_expr.span.hi()); + let snippet = self + .tcx + .sess + .source_map() + .span_to_snippet(sugg_span) + .unwrap_or_else(|_| method_name.to_string()); let (suggestion, applicability) = if has_params.unwrap_or_default() { - (format!("{}(...)", method_name), Applicability::HasPlaceholders) + (format!("{}(...)", snippet), Applicability::HasPlaceholders) } else { - (format!("{}()", method_name), Applicability::MaybeIncorrect) + (format!("{}()", snippet), Applicability::MaybeIncorrect) }; - err.span_suggestion(method_name.span, msg, suggestion, applicability); + err.span_suggestion(sugg_span, msg, suggestion, applicability); } /// Performs method lookup. If lookup is successful, it will return the callee diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs index af1c50acb0a35..eb8eabe1c03eb 100644 --- a/src/librustdoc/theme.rs +++ b/src/librustdoc/theme.rs @@ -179,20 +179,23 @@ fn get_previous_positions(events: &[Events], mut pos: usize) -> Vec { } fn build_rule(v: &[u8], positions: &[usize]) -> String { - positions - .chunks(2) - .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("")) - .collect::() - .trim() - .replace("\n", " ") - .replace("/", "") - .replace("\t", " ") - .replace("{", "") - .replace("}", "") - .split(' ') - .filter(|s| s.len() > 0) - .collect::>() - .join(" ") + minifier::css::minify( + &positions + .chunks(2) + .map(|x| ::std::str::from_utf8(&v[x[0]..x[1]]).unwrap_or("")) + .collect::() + .trim() + .replace("\n", " ") + .replace("/", "") + .replace("\t", " ") + .replace("{", "") + .replace("}", "") + .split(' ') + .filter(|s| s.len() > 0) + .collect::>() + .join(" "), + ) + .unwrap_or_else(|_| String::new()) } fn inner(v: &[u8], events: &[Events], pos: &mut usize) -> FxHashSet { diff --git a/src/librustdoc/theme/tests.rs b/src/librustdoc/theme/tests.rs index 9ead1d28007c1..b924215733d55 100644 --- a/src/librustdoc/theme/tests.rs +++ b/src/librustdoc/theme/tests.rs @@ -102,3 +102,16 @@ fn check_invalid_css() { let events = load_css_events(b"*"); assert_eq!(events.len(), 0); } + +#[test] +fn test_with_minification() { + let text = include_str!("../html/static/themes/dark.css"); + let minified = minifier::css::minify(&text).expect("CSS minification failed"); + + let against = load_css_paths(text.as_bytes()); + let other = load_css_paths(minified.as_bytes()); + + let mut ret = Vec::new(); + get_differences(&against, &other, &mut ret); + assert!(ret.is_empty()); +} diff --git a/src/libstd/backtrace.rs b/src/libstd/backtrace.rs index 5ba1c940251dc..a1c9aa75d779a 100644 --- a/src/libstd/backtrace.rs +++ b/src/libstd/backtrace.rs @@ -159,6 +159,69 @@ enum BytesOrWide { Wide(Vec), } +impl fmt::Debug for Backtrace { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut capture = match &self.inner { + Inner::Unsupported => return fmt.write_str("unsupported backtrace"), + Inner::Disabled => return fmt.write_str("disabled backtrace"), + Inner::Captured(c) => c.lock().unwrap(), + }; + capture.resolve(); + + let frames = &capture.frames[capture.actual_start..]; + + write!(fmt, "Backtrace ")?; + + let mut dbg = fmt.debug_list(); + + for frame in frames { + if frame.frame.ip().is_null() { + continue; + } + + dbg.entries(&frame.symbols); + } + + dbg.finish() + } +} + +impl fmt::Debug for BacktraceSymbol { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{{ ")?; + + if let Some(fn_name) = self.name.as_ref().map(|b| backtrace::SymbolName::new(b)) { + write!(fmt, "fn: \"{:#}\"", fn_name)?; + } else { + write!(fmt, "fn: \"\"")?; + } + + if let Some(fname) = self.filename.as_ref() { + write!(fmt, ", file: {:?}", fname)?; + } + + if let Some(line) = self.lineno.as_ref() { + write!(fmt, ", line: {:?}", line)?; + } + + write!(fmt, " }}") + } +} + +impl fmt::Debug for BytesOrWide { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + output_filename( + fmt, + match self { + BytesOrWide::Bytes(w) => BytesOrWideString::Bytes(w), + BytesOrWide::Wide(w) => BytesOrWideString::Wide(w), + }, + backtrace::PrintFmt::Short, + crate::env::current_dir().as_ref().ok(), + ) + } +} + impl Backtrace { /// Returns whether backtrace captures are enabled through environment /// variables. @@ -267,12 +330,6 @@ impl Backtrace { } impl fmt::Display for Backtrace { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self, fmt) - } -} - -impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let mut capture = match &self.inner { Inner::Unsupported => return fmt.write_str("unsupported backtrace"), diff --git a/src/libstd/sys/sgx/abi/entry.S b/src/libstd/sys/sgx/abi/entry.S index a3e059e813173..ed4db287229dc 100644 --- a/src/libstd/sys/sgx/abi/entry.S +++ b/src/libstd/sys/sgx/abi/entry.S @@ -30,6 +30,14 @@ IMAGE_BASE: /* We can store a bunch of data in the gap between MXCSR and the XSAVE header */ +/* MXCSR initialization value for ABI */ +.Lmxcsr_init: + .int 0x1f80 + +/* x87 FPU control word initialization value for ABI */ +.Lfpucw_init: + .int 0x037f + /* The following symbols point at read-only data that will be filled in by the */ /* post-linker. */ @@ -134,6 +142,19 @@ elf_entry: ud2 /* should not be reached */ /* end elf_entry */ +/* This code needs to be called *after* the enclave stack has been setup. */ +/* There are 3 places where this needs to happen, so this is put in a macro. */ +.macro entry_sanitize_final +/* Sanitize rflags received from user */ +/* - DF flag: x86-64 ABI requires DF to be unset at function entry/exit */ +/* - AC flag: AEX on misaligned memory accesses leaks side channel info */ + pushfq + andq $~0x40400, (%rsp) + popfq + bt $0,.Laborted(%rip) + jc .Lreentry_panic +.endm + .text .global sgx_entry .type sgx_entry,function @@ -150,25 +171,18 @@ sgx_entry: stmxcsr %gs:tcsls_user_mxcsr fnstcw %gs:tcsls_user_fcw -/* reset user state */ -/* - DF flag: x86-64 ABI requires DF to be unset at function entry/exit */ -/* - AC flag: AEX on misaligned memory accesses leaks side channel info */ - pushfq - andq $~0x40400, (%rsp) - popfq - /* check for debug buffer pointer */ testb $0xff,DEBUG(%rip) jz .Lskip_debug_init mov %r10,%gs:tcsls_debug_panic_buf_ptr .Lskip_debug_init: -/* check for abort */ - bt $0,.Laborted(%rip) - jc .Lreentry_panic /* check if returning from usercall */ mov %gs:tcsls_last_rsp,%r11 test %r11,%r11 jnz .Lusercall_ret +/* reset user state */ + ldmxcsr .Lmxcsr_init(%rip) + fldcw .Lfpucw_init(%rip) /* setup stack */ mov %gs:tcsls_tos,%rsp /* initially, RSP is not set to the correct value */ /* here. This is fixed below under "adjust stack". */ @@ -179,6 +193,7 @@ sgx_entry: lea IMAGE_BASE(%rip),%rax add %rax,%rsp mov %rsp,%gs:tcsls_tos + entry_sanitize_final /* call tcs_init */ /* store caller-saved registers in callee-saved registers */ mov %rdi,%rbx @@ -194,7 +209,10 @@ sgx_entry: mov %r13,%rdx mov %r14,%r8 mov %r15,%r9 + jmp .Lafter_init .Lskip_init: + entry_sanitize_final +.Lafter_init: /* call into main entry point */ load_tcsls_flag_secondary_bool cx /* RCX = entry() argument: secondary: bool */ call entry /* RDI, RSI, RDX, R8, R9 passed in from userspace */ @@ -295,6 +313,7 @@ usercall: ldmxcsr (%rsp) fldcw 4(%rsp) add $8, %rsp + entry_sanitize_final pop %rbx pop %rbp pop %r12 diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 4ac7e0e6e1fc0..15e2251d76321 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -12,6 +12,11 @@ #include "llvm/IR/AutoUpgrade.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Verifier.h" +#include "llvm/Passes/PassBuilder.h" +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Passes/StandardInstrumentations.h" +#endif #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -32,9 +37,12 @@ #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h" #include "llvm/Transforms/Instrumentation/MemorySanitizer.h" #endif +#if LLVM_VERSION_GE(9, 0) +#include "llvm/Transforms/Utils/CanonicalizeAliases.h" +#endif +#include "llvm/Transforms/Utils/NameAnonGlobals.h" using namespace llvm; -using namespace llvm::legacy; typedef struct LLVMOpaquePass *LLVMPassRef; typedef struct LLVMOpaqueTargetMachine *LLVMTargetMachineRef; @@ -314,6 +322,34 @@ static CodeGenOpt::Level fromRust(LLVMRustCodeGenOptLevel Level) { } } +enum class LLVMRustPassBuilderOptLevel { + O0, + O1, + O2, + O3, + Os, + Oz, +}; + +static PassBuilder::OptimizationLevel fromRust(LLVMRustPassBuilderOptLevel Level) { + switch (Level) { + case LLVMRustPassBuilderOptLevel::O0: + return PassBuilder::O0; + case LLVMRustPassBuilderOptLevel::O1: + return PassBuilder::O1; + case LLVMRustPassBuilderOptLevel::O2: + return PassBuilder::O2; + case LLVMRustPassBuilderOptLevel::O3: + return PassBuilder::O3; + case LLVMRustPassBuilderOptLevel::Os: + return PassBuilder::Os; + case LLVMRustPassBuilderOptLevel::Oz: + return PassBuilder::Oz; + default: + report_fatal_error("Bad PassBuilderOptLevel."); + } +} + enum class LLVMRustRelocMode { Default, Static, @@ -604,6 +640,212 @@ LLVMRustWriteOutputFile(LLVMTargetMachineRef Target, LLVMPassManagerRef PMR, return LLVMRustResult::Success; } +enum class LLVMRustOptStage { + PreLinkNoLTO, + PreLinkThinLTO, + PreLinkFatLTO, + ThinLTO, + FatLTO, +}; + +struct LLVMRustSanitizerOptions { + bool SanitizeMemory; + bool SanitizeThread; + bool SanitizeAddress; + bool SanitizeRecover; + int SanitizeMemoryTrackOrigins; +}; + +extern "C" void +LLVMRustOptimizeWithNewPassManager( + LLVMModuleRef ModuleRef, + LLVMTargetMachineRef TMRef, + LLVMRustPassBuilderOptLevel OptLevelRust, + LLVMRustOptStage OptStage, + bool NoPrepopulatePasses, bool VerifyIR, bool UseThinLTOBuffers, + bool MergeFunctions, bool UnrollLoops, bool SLPVectorize, bool LoopVectorize, + bool DisableSimplifyLibCalls, + LLVMRustSanitizerOptions *SanitizerOptions, + const char *PGOGenPath, const char *PGOUsePath) { +#if LLVM_VERSION_GE(9, 0) + Module *TheModule = unwrap(ModuleRef); + TargetMachine *TM = unwrap(TMRef); + PassBuilder::OptimizationLevel OptLevel = fromRust(OptLevelRust); + + // FIXME: MergeFunctions is not supported by NewPM yet. + (void) MergeFunctions; + + PipelineTuningOptions PTO; + PTO.LoopUnrolling = UnrollLoops; + PTO.LoopInterleaving = UnrollLoops; + PTO.LoopVectorization = LoopVectorize; + PTO.SLPVectorization = SLPVectorize; + + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + Optional PGOOpt; + if (PGOGenPath) { + assert(!PGOUsePath); + PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr); + } else if (PGOUsePath) { + assert(!PGOGenPath); + PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse); + } + + PassBuilder PB(TM, PTO, PGOOpt, &PIC); + + // FIXME: We may want to expose this as an option. + bool DebugPassManager = false; + LoopAnalysisManager LAM(DebugPassManager); + FunctionAnalysisManager FAM(DebugPassManager); + CGSCCAnalysisManager CGAM(DebugPassManager); + ModuleAnalysisManager MAM(DebugPassManager); + + FAM.registerPass([&] { return PB.buildDefaultAAPipeline(); }); + + Triple TargetTriple(TheModule->getTargetTriple()); + std::unique_ptr TLII(new TargetLibraryInfoImpl(TargetTriple)); + if (DisableSimplifyLibCalls) + TLII->disableAllFunctions(); + FAM.registerPass([&] { return TargetLibraryAnalysis(*TLII); }); + + PB.registerModuleAnalyses(MAM); + PB.registerCGSCCAnalyses(CGAM); + PB.registerFunctionAnalyses(FAM); + PB.registerLoopAnalyses(LAM); + PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); + + // We manually collect pipeline callbacks so we can apply them at O0, where the + // PassBuilder does not create a pipeline. + std::vector> PipelineStartEPCallbacks; + std::vector> + OptimizerLastEPCallbacks; + + if (VerifyIR) { + PipelineStartEPCallbacks.push_back([VerifyIR](ModulePassManager &MPM) { + MPM.addPass(VerifierPass()); + }); + } + + if (SanitizerOptions) { + if (SanitizerOptions->SanitizeMemory) { + MemorySanitizerOptions Options( + SanitizerOptions->SanitizeMemoryTrackOrigins, + SanitizerOptions->SanitizeRecover, + /*CompileKernel=*/false); +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([Options](ModulePassManager &MPM) { + MPM.addPass(MemorySanitizerPass(Options)); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [Options](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(MemorySanitizerPass(Options)); + } + ); + } + + if (SanitizerOptions->SanitizeThread) { +#if LLVM_VERSION_GE(10, 0) + PipelineStartEPCallbacks.push_back([](ModulePassManager &MPM) { + MPM.addPass(ThreadSanitizerPass()); + }); +#endif + OptimizerLastEPCallbacks.push_back( + [](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(ThreadSanitizerPass()); + } + ); + } + + if (SanitizerOptions->SanitizeAddress) { + // FIXME: Rust does not expose the UseAfterScope option. + PipelineStartEPCallbacks.push_back([&](ModulePassManager &MPM) { + MPM.addPass(RequireAnalysisPass()); + }); + OptimizerLastEPCallbacks.push_back( + [SanitizerOptions](FunctionPassManager &FPM, PassBuilder::OptimizationLevel Level) { + FPM.addPass(AddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover)); + } + ); + PipelineStartEPCallbacks.push_back( + [SanitizerOptions](ModulePassManager &MPM) { + MPM.addPass(ModuleAddressSanitizerPass( + /*CompileKernel=*/false, SanitizerOptions->SanitizeRecover)); + } + ); + } + } + + ModulePassManager MPM(DebugPassManager); + if (!NoPrepopulatePasses) { + if (OptLevel == PassBuilder::O0) { + for (const auto &C : PipelineStartEPCallbacks) + C(MPM); + + if (!OptimizerLastEPCallbacks.empty()) { + FunctionPassManager FPM(DebugPassManager); + for (const auto &C : OptimizerLastEPCallbacks) + C(FPM, OptLevel); + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + } + + MPM.addPass(AlwaysInlinerPass(/*InsertLifetimeIntrinsics=*/false)); + +#if LLVM_VERSION_GE(10, 0) + if (PGOOpt) { + PB.addPGOInstrPassesForO0( + MPM, DebugPassManager, PGOOpt->Action == PGOOptions::IRInstr, + /*IsCS=*/false, PGOOpt->ProfileFile, PGOOpt->ProfileRemappingFile); + } +#endif + } else { + for (const auto &C : PipelineStartEPCallbacks) + PB.registerPipelineStartEPCallback(C); + for (const auto &C : OptimizerLastEPCallbacks) + PB.registerOptimizerLastEPCallback(C); + + switch (OptStage) { + case LLVMRustOptStage::PreLinkNoLTO: + MPM = PB.buildPerModuleDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::PreLinkThinLTO: + MPM = PB.buildThinLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::PreLinkFatLTO: + MPM = PB.buildLTOPreLinkDefaultPipeline(OptLevel, DebugPassManager); + break; + case LLVMRustOptStage::ThinLTO: + // FIXME: Does it make sense to pass the ModuleSummaryIndex? + // It only seems to be needed for C++ specific optimizations. + MPM = PB.buildThinLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + case LLVMRustOptStage::FatLTO: + MPM = PB.buildLTODefaultPipeline(OptLevel, DebugPassManager, nullptr); + break; + } + } + } + + if (UseThinLTOBuffers) { + MPM.addPass(CanonicalizeAliasesPass()); + MPM.addPass(NameAnonGlobalPass()); + } + + // Upgrade all calls to old intrinsics first. + for (Module::iterator I = TheModule->begin(), E = TheModule->end(); I != E;) + UpgradeCallsToIntrinsic(&*I++); // must be post-increment, as we remove + + MPM.run(*TheModule, MAM); +#else + // The new pass manager has been available for a long time, + // but we don't bother supporting it on old LLVM versions. + report_fatal_error("New pass manager only supported since LLVM 9"); +#endif +} // Callback to demangle function name // Parameters: diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 46e467011b91a..49b6e1bfec38d 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -1296,6 +1296,14 @@ extern "C" LLVMValueRef LLVMRustBuildMemMove(LLVMBuilderRef B, #endif } +extern "C" LLVMValueRef LLVMRustBuildMemSet(LLVMBuilderRef B, + LLVMValueRef Dst, unsigned DstAlign, + LLVMValueRef Val, + LLVMValueRef Size, bool IsVolatile) { + return wrap(unwrap(B)->CreateMemSet( + unwrap(Dst), unwrap(Val), unwrap(Size), DstAlign, IsVolatile)); +} + extern "C" LLVMValueRef LLVMRustBuildInvoke(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args, unsigned NumArgs, LLVMBasicBlockRef Then, diff --git a/src/test/codegen/sanitizer-memory-track-orgins.rs b/src/test/codegen/sanitizer-memory-track-orgins.rs index 1fd496b35dfcc..8ea41c5d44bb1 100644 --- a/src/test/codegen/sanitizer-memory-track-orgins.rs +++ b/src/test/codegen/sanitizer-memory-track-orgins.rs @@ -15,10 +15,10 @@ #![crate_type="lib"] // MSAN-0-NOT: @__msan_track_origins -// MSAN-1: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1 -// MSAN-2: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2 -// MSAN-1-LTO: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 1 -// MSAN-2-LTO: @__msan_track_origins = weak_odr local_unnamed_addr constant i32 2 +// MSAN-1: @__msan_track_origins = weak_odr {{.*}}constant i32 1 +// MSAN-2: @__msan_track_origins = weak_odr {{.*}}constant i32 2 +// MSAN-1-LTO: @__msan_track_origins = weak_odr {{.*}}constant i32 1 +// MSAN-2-LTO: @__msan_track_origins = weak_odr {{.*}}constant i32 2 // // MSAN-0-LABEL: define void @copy( // MSAN-1-LABEL: define void @copy( diff --git a/src/test/ui/consts/const-err2.rs b/src/test/ui/consts/const-err2.rs index 351dfd2e0f58c..7c5aaedda35fa 100644 --- a/src/test/ui/consts/const-err2.rs +++ b/src/test/ui/consts/const-err2.rs @@ -17,16 +17,22 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; //~^ ERROR const_err + let a_i128 = -std::i128::MIN; + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; //~^ ERROR const_err + let b_i128 = std::i128::MIN - std::i128::MAX; + //~^ ERROR const_err let c = 200u8 * 4; //~^ ERROR const_err let d = 42u8 - (42u8 + 1); //~^ ERROR const_err let _e = [5u8][1]; - //~^ ERROR index out of bounds + //~^ ERROR const_err black_box(a); + black_box(a_i128); black_box(b); + black_box(b_i128); black_box(c); black_box(d); } diff --git a/src/test/ui/consts/const-err2.stderr b/src/test/ui/consts/const-err2.stderr index a76b6d1775f04..f135bf0b06cad 100644 --- a/src/test/ui/consts/const-err2.stderr +++ b/src/test/ui/consts/const-err2.stderr @@ -11,28 +11,40 @@ LL | #![deny(const_err)] | ^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/const-err2.rs:20:13 + --> $DIR/const-err2.rs:20:18 + | +LL | let a_i128 = -std::i128::MIN; + | ^^^^^^^^^^^^^^^ attempt to negate with overflow + +error: this expression will panic at runtime + --> $DIR/const-err2.rs:22:13 | LL | let b = 200u8 + 200u8 + 200u8; | ^^^^^^^^^^^^^ attempt to add with overflow error: this expression will panic at runtime - --> $DIR/const-err2.rs:22:13 + --> $DIR/const-err2.rs:24:18 + | +LL | let b_i128 = std::i128::MIN - std::i128::MAX; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow + +error: this expression will panic at runtime + --> $DIR/const-err2.rs:26:13 | LL | let c = 200u8 * 4; | ^^^^^^^^^ attempt to multiply with overflow error: this expression will panic at runtime - --> $DIR/const-err2.rs:24:13 + --> $DIR/const-err2.rs:28:13 | LL | let d = 42u8 - (42u8 + 1); | ^^^^^^^^^^^^^^^^^ attempt to subtract with overflow error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/const-err2.rs:26:14 + --> $DIR/const-err2.rs:30:14 | LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/consts/const-err3.rs b/src/test/ui/consts/const-err3.rs index ab3823efd301d..43aba4a8b012b 100644 --- a/src/test/ui/consts/const-err3.rs +++ b/src/test/ui/consts/const-err3.rs @@ -17,8 +17,12 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; //~^ ERROR const_err + let a_i128 = -std::i128::MIN; + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; //~^ ERROR const_err + let b_i128 = std::i128::MIN - std::i128::MAX; + //~^ ERROR const_err let c = 200u8 * 4; //~^ ERROR const_err let d = 42u8 - (42u8 + 1); @@ -26,7 +30,9 @@ fn main() { let _e = [5u8][1]; //~^ ERROR const_err black_box(a); + black_box(a_i128); black_box(b); + black_box(b_i128); black_box(c); black_box(d); } diff --git a/src/test/ui/consts/const-err3.stderr b/src/test/ui/consts/const-err3.stderr index 02b912e928c80..05f64b87fcce6 100644 --- a/src/test/ui/consts/const-err3.stderr +++ b/src/test/ui/consts/const-err3.stderr @@ -10,29 +10,41 @@ note: the lint level is defined here LL | #![deny(const_err)] | ^^^^^^^^^ +error: attempt to negate with overflow + --> $DIR/const-err3.rs:20:18 + | +LL | let a_i128 = -std::i128::MIN; + | ^^^^^^^^^^^^^^^ + error: attempt to add with overflow - --> $DIR/const-err3.rs:20:13 + --> $DIR/const-err3.rs:22:13 | LL | let b = 200u8 + 200u8 + 200u8; | ^^^^^^^^^^^^^ +error: attempt to subtract with overflow + --> $DIR/const-err3.rs:24:18 + | +LL | let b_i128 = std::i128::MIN - std::i128::MAX; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: attempt to multiply with overflow - --> $DIR/const-err3.rs:22:13 + --> $DIR/const-err3.rs:26:13 | LL | let c = 200u8 * 4; | ^^^^^^^^^ error: attempt to subtract with overflow - --> $DIR/const-err3.rs:24:13 + --> $DIR/const-err3.rs:28:13 | LL | let d = 42u8 - (42u8 + 1); | ^^^^^^^^^^^^^^^^^ error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/const-err3.rs:26:14 + --> $DIR/const-err3.rs:30:14 | LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/consts/const-int-arithmetic-overflow.rs b/src/test/ui/consts/const-int-arithmetic-overflow.rs new file mode 100644 index 0000000000000..75dac812f1e3a --- /dev/null +++ b/src/test/ui/consts/const-int-arithmetic-overflow.rs @@ -0,0 +1,26 @@ +// run-pass +// compile-flags: -O +#![allow(const_err)] + +// Make sure arithmetic unary/binary ops actually return the right result, even when overflowing. +// We have to put them in `const fn` and turn on optimizations to avoid overflow checks. + +const fn add(x: i8, y: i8) -> i8 { x+y } +const fn sub(x: i8, y: i8) -> i8 { x-y } +const fn mul(x: i8, y: i8) -> i8 { x*y } +// div and rem are always checked, so we cannot test their result in case of oveflow. +const fn neg(x: i8) -> i8 { -x } + +fn main() { + const ADD_OFLOW: i8 = add(100, 100); + assert_eq!(ADD_OFLOW, -56); + + const SUB_OFLOW: i8 = sub(100, -100); + assert_eq!(SUB_OFLOW, -56); + + const MUL_OFLOW: i8 = mul(-100, -2); + assert_eq!(MUL_OFLOW, -56); + + const NEG_OFLOW: i8 = neg(-128); + assert_eq!(NEG_OFLOW, -128); +} diff --git a/src/test/ui/consts/const-int-arithmetic.rs b/src/test/ui/consts/const-int-arithmetic.rs index cfa2873c68bad..2c3421b7a8d0b 100644 --- a/src/test/ui/consts/const-int-arithmetic.rs +++ b/src/test/ui/consts/const-int-arithmetic.rs @@ -7,7 +7,7 @@ #![feature(const_saturating_int_methods)] #![feature(const_wrapping_int_methods)] -use std::i8; +use std::{i8, i128}; macro_rules! suite { ($( @@ -65,6 +65,10 @@ suite!( C26: 5i8.checked_rem_euclid(0), None; C27: i8::MIN.checked_rem_euclid(-1), None; } + checked_i128 -> Option { + CHK_ADD_I128: i128::MAX.checked_add(1), None; + CHK_MUL_I128: i128::MIN.checked_mul(-1), None; + } saturating_and_wrapping -> i8 { // `const_saturating_int_methods` @@ -104,6 +108,13 @@ suite!( C47: 100i8.wrapping_rem_euclid(10), 0; C48: (-128i8).wrapping_rem_euclid(-1), 0; } + saturating_and_wrapping_i128 -> i128 { + SAT_ADD_I128: i128::MAX.saturating_add(1), i128::MAX; + SAT_MUL_I128: i128::MAX.saturating_mul(2), i128::MAX; + + WRP_ADD_I128: i128::MAX.wrapping_add(1), i128::MIN; + WRP_MUL_I128: i128::MAX.wrapping_mul(3), i128::MAX-2; + } overflowing -> (i8, bool) { // `const_overflowing_int_methods` @@ -119,12 +130,18 @@ suite!( C55: 5i8.overflowing_rem_euclid(2), (1, false); C56: i8::MIN.overflowing_rem_euclid(-1), (0, true); - + } + overflowing_i128 -> (i128, bool) { + OFL_ADD_I128: i128::MAX.overflowing_add(1), (i128::MIN, true); + OFL_MUL_I128: i128::MAX.overflowing_mul(3), (i128::MAX-2, true); } ); fn main() { checked(); + checked_i128(); saturating_and_wrapping(); + saturating_and_wrapping_i128(); overflowing(); + overflowing_i128(); } diff --git a/src/test/ui/issues/issue-8460-const.rs b/src/test/ui/issues/issue-8460-const.rs index c18a0d4d6cbbe..5866cef2d2c70 100644 --- a/src/test/ui/issues/issue-8460-const.rs +++ b/src/test/ui/issues/issue-8460-const.rs @@ -3,7 +3,7 @@ #![deny(const_err)] -use std::{isize, i8, i16, i32, i64}; +use std::{isize, i8, i16, i32, i64, i128}; use std::thread; fn main() { @@ -22,6 +22,9 @@ fn main() { assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR this expression will panic at runtime + assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + //~^ ERROR attempt to divide with overflow + //~| ERROR this expression will panic at runtime assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -32,6 +35,8 @@ fn main() { //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR this expression will panic at runtime @@ -47,6 +52,9 @@ fn main() { assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR this expression will panic at runtime + assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR this expression will panic at runtime assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); @@ -57,4 +65,6 @@ fn main() { //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with a divisor of zero } diff --git a/src/test/ui/issues/issue-8460-const.stderr b/src/test/ui/issues/issue-8460-const.stderr index 6b1d74094a10b..d7373948cb9e0 100644 --- a/src/test/ui/issues/issue-8460-const.stderr +++ b/src/test/ui/issues/issue-8460-const.stderr @@ -64,125 +64,161 @@ error: this expression will panic at runtime LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to divide with overflow -error: attempt to divide by zero +error: attempt to divide with overflow + --> $DIR/issue-8460-const.rs:25:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: this expression will panic at runtime --> $DIR/issue-8460-const.rs:25:36 | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ attempt to divide with overflow + +error: attempt to divide by zero + --> $DIR/issue-8460-const.rs:28:36 + | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:27:36 + --> $DIR/issue-8460-const.rs:30:36 | LL | assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); | ^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:29:36 + --> $DIR/issue-8460-const.rs:32:36 | LL | assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:31:36 + --> $DIR/issue-8460-const.rs:34:36 | LL | assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:33:36 + --> $DIR/issue-8460-const.rs:36:36 | LL | assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); | ^^^^^^^^ +error: attempt to divide by zero + --> $DIR/issue-8460-const.rs:38:36 + | +LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + | ^^^^^^^^^ + error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:35:36 + --> $DIR/issue-8460-const.rs:40:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:35:36 + --> $DIR/issue-8460-const.rs:40:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:38:36 + --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:38:36 + --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:41:36 + --> $DIR/issue-8460-const.rs:46:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:41:36 + --> $DIR/issue-8460-const.rs:46:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:44:36 + --> $DIR/issue-8460-const.rs:49:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:44:36 + --> $DIR/issue-8460-const.rs:49:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:47:36 + --> $DIR/issue-8460-const.rs:52:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:47:36 + --> $DIR/issue-8460-const.rs:52:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow +error: attempt to calculate the remainder with overflow + --> $DIR/issue-8460-const.rs:55:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: this expression will panic at runtime + --> $DIR/issue-8460-const.rs:55:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ attempt to calculate the remainder with overflow + error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:50:36 + --> $DIR/issue-8460-const.rs:58:36 | LL | assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:52:36 + --> $DIR/issue-8460-const.rs:60:36 | LL | assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); | ^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:54:36 + --> $DIR/issue-8460-const.rs:62:36 | LL | assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:56:36 + --> $DIR/issue-8460-const.rs:64:36 | LL | assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:58:36 + --> $DIR/issue-8460-const.rs:66:36 | LL | assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); | ^^^^^^^^ -error: aborting due to 30 previous errors +error: attempt to calculate the remainder with a divisor of zero + --> $DIR/issue-8460-const.rs:68:36 + | +LL | assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + | ^^^^^^^^^ + +error: aborting due to 36 previous errors diff --git a/src/test/ui/issues/issue-8460-const2.rs b/src/test/ui/issues/issue-8460-const2.rs index 0ca850abc1b60..afea859bb65a9 100644 --- a/src/test/ui/issues/issue-8460-const2.rs +++ b/src/test/ui/issues/issue-8460-const2.rs @@ -3,7 +3,7 @@ #![deny(const_err)] -use std::{isize, i8, i16, i32, i64}; +use std::{isize, i8, i16, i32, i64, i128}; use std::thread; fn main() { @@ -17,6 +17,8 @@ fn main() { //~^ ERROR attempt to divide with overflow assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + //~^ ERROR attempt to divide with overflow assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -27,6 +29,8 @@ fn main() { //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); @@ -37,6 +41,8 @@ fn main() { //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); @@ -47,4 +53,6 @@ fn main() { //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with a divisor of zero } diff --git a/src/test/ui/issues/issue-8460-const2.stderr b/src/test/ui/issues/issue-8460-const2.stderr index 63b9123e95097..e25d560fe0ce3 100644 --- a/src/test/ui/issues/issue-8460-const2.stderr +++ b/src/test/ui/issues/issue-8460-const2.stderr @@ -34,95 +34,119 @@ error: attempt to divide with overflow LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ -error: attempt to divide by zero +error: attempt to divide with overflow --> $DIR/issue-8460-const2.rs:20:36 | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: attempt to divide by zero + --> $DIR/issue-8460-const2.rs:22:36 + | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:22:36 + --> $DIR/issue-8460-const2.rs:24:36 | LL | assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); | ^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:24:36 + --> $DIR/issue-8460-const2.rs:26:36 | LL | assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:26:36 + --> $DIR/issue-8460-const2.rs:28:36 | LL | assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:28:36 + --> $DIR/issue-8460-const2.rs:30:36 | LL | assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); | ^^^^^^^^ +error: attempt to divide by zero + --> $DIR/issue-8460-const2.rs:32:36 + | +LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + | ^^^^^^^^^ + error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:30:36 + --> $DIR/issue-8460-const2.rs:34:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:32:36 + --> $DIR/issue-8460-const2.rs:36:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:34:36 + --> $DIR/issue-8460-const2.rs:38:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:36:36 + --> $DIR/issue-8460-const2.rs:40:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:38:36 + --> $DIR/issue-8460-const2.rs:42:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ +error: attempt to calculate the remainder with overflow + --> $DIR/issue-8460-const2.rs:44:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:40:36 + --> $DIR/issue-8460-const2.rs:46:36 | LL | assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:42:36 + --> $DIR/issue-8460-const2.rs:48:36 | LL | assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); | ^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:44:36 + --> $DIR/issue-8460-const2.rs:50:36 | LL | assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:46:36 + --> $DIR/issue-8460-const2.rs:52:36 | LL | assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:48:36 + --> $DIR/issue-8460-const2.rs:54:36 | LL | assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); | ^^^^^^^^ -error: aborting due to 20 previous errors +error: attempt to calculate the remainder with a divisor of zero + --> $DIR/issue-8460-const2.rs:56:36 + | +LL | assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + | ^^^^^^^^^ + +error: aborting due to 24 previous errors diff --git a/src/test/ui/parser/type-parameters-in-field-exprs.rs b/src/test/ui/parser/type-parameters-in-field-exprs.rs index 6a3b2c1c60605..4cd77ebbd862f 100644 --- a/src/test/ui/parser/type-parameters-in-field-exprs.rs +++ b/src/test/ui/parser/type-parameters-in-field-exprs.rs @@ -9,9 +9,9 @@ fn main() { y: 2, }; f.x::; - //~^ ERROR field expressions may not have generic arguments + //~^ ERROR field expressions cannot have generic arguments f.x::<>; - //~^ ERROR field expressions may not have generic arguments + //~^ ERROR field expressions cannot have generic arguments f.x::(); - //~^ ERROR field expressions may not have generic arguments + //~^ ERROR field expressions cannot have generic arguments } diff --git a/src/test/ui/parser/type-parameters-in-field-exprs.stderr b/src/test/ui/parser/type-parameters-in-field-exprs.stderr index 306b4754d0d69..ce7364d3534f6 100644 --- a/src/test/ui/parser/type-parameters-in-field-exprs.stderr +++ b/src/test/ui/parser/type-parameters-in-field-exprs.stderr @@ -1,16 +1,16 @@ -error: field expressions may not have generic arguments +error: field expressions cannot have generic arguments --> $DIR/type-parameters-in-field-exprs.rs:11:10 | LL | f.x::; | ^^^^^^^ -error: field expressions may not have generic arguments +error: field expressions cannot have generic arguments --> $DIR/type-parameters-in-field-exprs.rs:13:10 | LL | f.x::<>; | ^^ -error: field expressions may not have generic arguments +error: field expressions cannot have generic arguments --> $DIR/type-parameters-in-field-exprs.rs:15:7 | LL | f.x::(); diff --git a/src/test/ui/suggestions/method-missing-parentheses.rs b/src/test/ui/suggestions/method-missing-parentheses.rs new file mode 100644 index 0000000000000..f10bfb56d2e12 --- /dev/null +++ b/src/test/ui/suggestions/method-missing-parentheses.rs @@ -0,0 +1,5 @@ +fn main() { + let _ = vec![].into_iter().collect::; + //~^ ERROR attempted to take value of method `collect` on type `std::vec::IntoIter<_>` + //~| ERROR field expressions cannot have generic arguments +} diff --git a/src/test/ui/suggestions/method-missing-parentheses.stderr b/src/test/ui/suggestions/method-missing-parentheses.stderr new file mode 100644 index 0000000000000..6e4f7a84724bf --- /dev/null +++ b/src/test/ui/suggestions/method-missing-parentheses.stderr @@ -0,0 +1,17 @@ +error: field expressions cannot have generic arguments + --> $DIR/method-missing-parentheses.rs:2:41 + | +LL | let _ = vec![].into_iter().collect::; + | ^^^^^^^ + +error[E0615]: attempted to take value of method `collect` on type `std::vec::IntoIter<_>` + --> $DIR/method-missing-parentheses.rs:2:32 + | +LL | let _ = vec![].into_iter().collect::; + | ^^^^^^^--------- + | | + | help: use parentheses to call the method: `collect::()` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0615`.