diff options
365 files changed, 4880 insertions, 1313 deletions
diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index c182f3245e5..d7a31c8e80f 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -42,7 +42,7 @@ jobs: # Exit with error if open and S-waiting-on-bors if [[ "$STATE" == "OPEN" && "$WAITING_ON_BORS" == "true" ]]; then - exit 1 + gh run cancel ${{ github.run_id }} fi update: @@ -65,7 +65,10 @@ jobs: - name: cargo update # Remove first line that always just says "Updating crates.io index" - run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + # If there are no changes, cancel the job here + run: | + cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log + git status --porcelain | grep -q Cargo.lock || gh run cancel ${{ github.run_id }} - name: upload Cargo.lock artifact for use in PR uses: actions/upload-artifact@v3 with: @@ -131,7 +134,7 @@ jobs: # Exit with error if PR is closed STATE=$(gh pr view cargo_update --repo $GITHUB_REPOSITORY --json state --jq '.state') if [[ "$STATE" != "OPEN" ]]; then - exit 1 + gh run cancel ${{ github.run_id }} fi gh pr edit cargo_update --title "${PR_TITLE}" --body-file body.md --repo $GITHUB_REPOSITORY diff --git a/compiler/rustc_ast_lowering/src/block.rs b/compiler/rustc_ast_lowering/src/block.rs index 11a66fe87c9..eef6e8280af 100644 --- a/compiler/rustc_ast_lowering/src/block.rs +++ b/compiler/rustc_ast_lowering/src/block.rs @@ -81,7 +81,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { (self.arena.alloc_from_iter(stmts), expr) } - fn lower_local(&mut self, l: &Local) -> &'hir hir::Local<'hir> { + fn lower_local(&mut self, l: &Local) -> &'hir hir::LetStmt<'hir> { let ty = l .ty .as_ref() @@ -97,7 +97,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let span = self.lower_span(l.span); let source = hir::LocalSource::Normal; self.lower_attrs(hir_id, &l.attrs); - self.arena.alloc(hir::Local { hir_id, ty, pat, init, els, span, source }) + self.arena.alloc(hir::LetStmt { hir_id, ty, pat, init, els, span, source }) } fn lower_block_check_mode(&mut self, b: &BlockCheckMode) -> hir::BlockCheckMode { diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 9078b1f889c..a1164008d0d 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -302,8 +302,8 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> { }); } - fn visit_local(&mut self, l: &'hir Local<'hir>) { - self.insert(l.span, l.hir_id, Node::Local(l)); + fn visit_local(&mut self, l: &'hir LetStmt<'hir>) { + self.insert(l.span, l.hir_id, Node::LetStmt(l)); self.with_parent(l.hir_id, |this| { intravisit::walk_local(this, l); }) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index d2744e8ff68..b5b98659e2f 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2341,7 +2341,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { debug_assert!(!a.is_empty()); self.attrs.insert(hir_id.local_id, a); } - let local = hir::Local { + let local = hir::LetStmt { hir_id, init, pat, diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index aa98d060f5f..35bd7d37992 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -622,7 +622,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // FIXME: We make sure that this is a normal top-level binding, // but we could suggest `todo!()` for all uninitalized bindings in the pattern pattern - if let hir::StmtKind::Let(hir::Local { span, ty, init: None, pat, .. }) = + if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) = &ex.kind && let hir::PatKind::Binding(..) = pat.kind && span.contains(self.decl_span) @@ -800,7 +800,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { for (_, node) in tcx.hir().parent_iter(expr.hir_id) { let e = match node { hir::Node::Expr(e) => e, - hir::Node::Local(hir::Local { els: Some(els), .. }) => { + hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => { let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] }; finder.visit_block(els); if !finder.found_breaks.is_empty() { @@ -2124,7 +2124,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { hir::intravisit::walk_expr(self, e); } - fn visit_local(&mut self, local: &'hir hir::Local<'hir>) { + fn visit_local(&mut self, local: &'hir hir::LetStmt<'hir>) { if let hir::Pat { kind: hir::PatKind::Binding(_, hir_id, _ident, _), .. } = local.pat && let Some(init) = local.init diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 11561539f6d..0106e285604 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,7 +4,6 @@ use crate::session_diagnostics::{ CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause, CaptureVarKind, CaptureVarPathUseCause, OnClosureNote, }; -use itertools::Itertools; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, Namespace}; @@ -226,16 +225,15 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } else { if autoderef_index.is_none() { - autoderef_index = - match place.projection.into_iter().rev().find_position(|elem| { - !matches!( - elem, - ProjectionElem::Deref | ProjectionElem::Downcast(..) - ) - }) { - Some((index, _)) => Some(place.projection.len() - index), - None => Some(0), - }; + autoderef_index = match place.projection.iter().rposition(|elem| { + !matches!( + elem, + ProjectionElem::Deref | ProjectionElem::Downcast(..) + ) + }) { + Some(index) => Some(index + 1), + None => Some(0), + }; } if index >= autoderef_index.unwrap() { buf.insert(0, '*'); diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 36c2723b66d..2aeea1dd341 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -558,7 +558,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hir::intravisit::walk_stmt(self, stmt); let expr = match stmt.kind { hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr, - hir::StmtKind::Let(hir::Local { init: Some(expr), .. }) => expr, + hir::StmtKind::Let(hir::LetStmt { init: Some(expr), .. }) => expr, _ => { return; } @@ -737,7 +737,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { && let body = self.infcx.tcx.hir().body(body_id) && let Some(hir_id) = (BindingFinder { span: pat_span }).visit_body(body).break_value() && let node = self.infcx.tcx.hir_node(hir_id) - && let hir::Node::Local(hir::Local { + && let hir::Node::LetStmt(hir::LetStmt { pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. }, .. }) @@ -1170,7 +1170,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { }; if let Some(hir_id) = hir_id - && let hir::Node::Local(local) = self.infcx.tcx.hir_node(hir_id) + && let hir::Node::LetStmt(local) = self.infcx.tcx.hir_node(hir_id) { let tables = self.infcx.tcx.typeck(def_id.as_local().unwrap()); if let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait() diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 700b5e13dec..a206aac0467 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2000,7 +2000,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ConstraintCategory::SizedBound, ); } - &Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {} + &Rvalue::NullaryOp(NullOp::UbChecks, _) => {} Rvalue::ShallowInitBox(operand, ty) => { self.check_operand(operand, location); diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 047dc56a32e..dbce6d165d2 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -780,7 +780,7 @@ fn codegen_stmt<'tcx>( NullOp::OffsetOf(fields) => { layout.offset_of_subfield(fx, fields.iter()).bytes() } - NullOp::UbCheck(_) => { + NullOp::UbChecks => { let val = fx.tcx.sess.opts.debug_assertions; let val = CValue::by_val( fx.bcx.ins().iconst(types::I8, i64::try_from(val).unwrap()), diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 9b679019e96..06b14a1f118 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -541,7 +541,7 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { let builtin_unreachable: RValue<'gcc> = unsafe { std::mem::transmute(builtin_unreachable) }; - self.call(self.type_void(), None, None, builtin_unreachable, &[], None); + self.call(self.type_void(), None, None, builtin_unreachable, &[], None, None); } // Write results to outputs. diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f5cda81f6ab..43cc46cfe68 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -25,7 +25,7 @@ use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout, }; -use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; +use rustc_middle::ty::{ParamEnv, Ty, TyCtxt, Instance}; use rustc_span::def_id::DefId; use rustc_span::Span; use rustc_target::abi::{ @@ -592,12 +592,13 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { then: Block<'gcc>, catch: Block<'gcc>, _funclet: Option<&Funclet>, + instance: Option<Instance<'tcx>>, ) -> RValue<'gcc> { let try_block = self.current_func().new_block("try"); let current_block = self.block.clone(); self.block = try_block; - let call = self.call(typ, fn_attrs, None, func, args, None); // TODO(antoyo): use funclet here? + let call = self.call(typ, fn_attrs, None, func, args, None, instance); // TODO(antoyo): use funclet here? self.block = current_block; let return_value = @@ -1667,6 +1668,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { func: RValue<'gcc>, args: &[RValue<'gcc>], funclet: Option<&Funclet>, + _instance: Option<Instance<'tcx>>, ) -> RValue<'gcc> { // FIXME(antoyo): remove when having a proper API. let gcc_func = unsafe { std::mem::transmute(func) }; diff --git a/compiler/rustc_codegen_gcc/src/coverageinfo.rs b/compiler/rustc_codegen_gcc/src/coverageinfo.rs index 849e9886ef3..4e44f78f23c 100644 --- a/compiler/rustc_codegen_gcc/src/coverageinfo.rs +++ b/compiler/rustc_codegen_gcc/src/coverageinfo.rs @@ -1,11 +1,11 @@ use rustc_codegen_ssa::traits::CoverageInfoBuilderMethods; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; use crate::builder::Builder; impl<'a, 'gcc, 'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { - fn add_coverage(&mut self, _instance: Instance<'tcx>, _coverage: &Coverage) { + fn add_coverage(&mut self, _instance: Instance<'tcx>, _kind: &CoverageKind) { // TODO(antoyo) } } diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index a6c8b72e851..cebd45c09aa 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -133,6 +133,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { func, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None, + None, ) } sym::likely => self.expect(args[0].immediate(), true), @@ -401,7 +402,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'a, 'gcc, 'tcx> { fn abort(&mut self) { let func = self.context.get_builtin_function("abort"); let func: RValue<'gcc> = unsafe { std::mem::transmute(func) }; - self.call(self.type_void(), None, None, func, &[], None); + self.call(self.type_void(), None, None, func, &[], None, None); } fn assume(&mut self, value: Self::Value) { @@ -1103,7 +1104,7 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: RValue<'gcc>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { - bx.call(bx.type_void(), None, None, try_func, &[data], None); + bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx.data_layout.i32_align.abi; @@ -1177,21 +1178,21 @@ fn codegen_gnu_try<'gcc>( let zero = bx.cx.context.new_rvalue_zero(bx.int_type); let ptr = bx.cx.context.new_call(None, eh_pointer_builtin, &[zero]); let catch_ty = bx.type_func(&[bx.type_i8p(), bx.type_i8p()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None); bx.ret(bx.const_i32(1)); // NOTE: the blocks must be filled before adding the try/catch, otherwise gcc will not // generate a try/catch. // FIXME(antoyo): add a check in the libgccjit API to prevent this. bx.switch_to_block(current_block); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); }); let func = unsafe { std::mem::transmute(func) }; // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 74539d4d495..500904ce188 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -466,11 +466,11 @@ pub(crate) fn inline_asm_call<'ll>( let call = if !labels.is_empty() { assert!(catch_funclet.is_none()); - bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None) + bx.callbr(fty, None, None, v, inputs, dest.unwrap(), labels, None, None) } else if let Some((catch, funclet)) = catch_funclet { - bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet) + bx.invoke(fty, None, None, v, inputs, dest.unwrap(), catch, funclet, None) } else { - bx.call(fty, None, None, v, inputs, None) + bx.call(fty, None, None, v, inputs, None, None) }; // Store mark in a metadata node so we can map LLVM errors diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 63e59ea13fc..a5a5ae73d77 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -19,9 +19,12 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use rustc_span::Span; -use rustc_symbol_mangling::typeid::{kcfi_typeid_for_fnabi, typeid_for_fnabi, TypeIdOptions}; +use rustc_symbol_mangling::typeid::{ + kcfi_typeid_for_fnabi, kcfi_typeid_for_instance, typeid_for_fnabi, typeid_for_instance, + TypeIdOptions, +}; use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange}; use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target}; use smallvec::SmallVec; @@ -221,6 +224,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { then: &'ll BasicBlock, catch: &'ll BasicBlock, funclet: Option<&Funclet<'ll>>, + instance: Option<Instance<'tcx>>, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -233,10 +237,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1231,6 +1235,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { llfn: &'ll Value, args: &[&'ll Value], funclet: Option<&Funclet<'ll>>, + instance: Option<Instance<'tcx>>, ) -> &'ll Value { debug!("call {:?} with args ({:?})", llfn, args); @@ -1243,10 +1248,10 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1468,7 +1473,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { pub(crate) fn call_intrinsic(&mut self, intrinsic: &str, args: &[&'ll Value]) -> &'ll Value { let (ty, f) = self.cx.get_intrinsic(intrinsic); - self.call(ty, None, None, f, args, None) + self.call(ty, None, None, f, args, None, None) } fn call_lifetime_intrinsic(&mut self, intrinsic: &str, ptr: &'ll Value, size: Size) { @@ -1526,7 +1531,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { format!("llvm.{instr}.sat.i{int_width}.f{float_width}") }; let f = self.declare_cfn(&name, llvm::UnnamedAddr::No, self.type_func(&[src_ty], dest_ty)); - self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None) + self.call(self.type_func(&[src_ty], dest_ty), None, None, f, &[val], None, None) } pub(crate) fn landing_pad( @@ -1554,6 +1559,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { default_dest: &'ll BasicBlock, indirect_dest: &[&'ll BasicBlock], funclet: Option<&Funclet<'ll>>, + instance: Option<Instance<'tcx>>, ) -> &'ll Value { debug!("invoke {:?} with args ({:?})", llfn, args); @@ -1566,10 +1572,10 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { } // Emit CFI pointer type membership test - self.cfi_type_test(fn_attrs, fn_abi, llfn); + self.cfi_type_test(fn_attrs, fn_abi, instance, llfn); // Emit KCFI operand bundle - let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, llfn); + let kcfi_bundle = self.kcfi_operand_bundle(fn_attrs, fn_abi, instance, llfn); let kcfi_bundle = kcfi_bundle.as_ref().map(|b| &*b.raw); if let Some(kcfi_bundle) = kcfi_bundle { bundles.push(kcfi_bundle); @@ -1601,6 +1607,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { &mut self, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + instance: Option<Instance<'tcx>>, llfn: &'ll Value, ) { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; @@ -1622,7 +1629,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let typeid = typeid_for_fnabi(self.tcx, fn_abi, options); + let typeid = if let Some(instance) = instance { + typeid_for_instance(self.tcx, &instance, options) + } else { + typeid_for_fnabi(self.tcx, fn_abi, options) + }; let typeid_metadata = self.cx.typeid_metadata(typeid).unwrap(); // Test whether the function pointer is associated with the type identifier. @@ -1644,6 +1655,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { &mut self, fn_attrs: Option<&CodegenFnAttrs>, fn_abi: Option<&FnAbi<'tcx, Ty<'tcx>>>, + instance: Option<Instance<'tcx>>, llfn: &'ll Value, ) -> Option<llvm::OperandBundleDef<'ll>> { let is_indirect_call = unsafe { llvm::LLVMRustIsNonGVFunctionPointerTy(llfn) }; @@ -1665,7 +1677,12 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { options.insert(TypeIdOptions::NORMALIZE_INTEGERS); } - let kcfi_typeid = kcfi_typeid_for_fnabi(self.tcx, fn_abi, options); + let kcfi_typeid = if let Some(instance) = instance { + kcfi_typeid_for_instance(self.tcx, &instance, options) + } else { + kcfi_typeid_for_fnabi(self.tcx, fn_abi, options) + }; + Some(llvm::OperandBundleDef::new("kcfi", &[self.const_u32(kcfi_typeid)])) } else { None diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 54f4bc06340..85277db6d53 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -14,7 +14,6 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_llvm::RustString; use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::Coverage; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::Instance; @@ -75,7 +74,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { #[instrument(level = "debug", skip(self))] - fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) { + fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) { // Our caller should have already taken care of inlining subtleties, // so we can assume that counter/expression IDs in this coverage // statement are meaningful for the given instance. @@ -98,7 +97,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> { .entry(instance) .or_insert_with(|| FunctionCoverageCollector::new(instance, function_coverage_info)); - let Coverage { kind } = coverage; match *kind { CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "marker statement {kind:?} should have been removed by CleanupPostBorrowck" diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 2409b2e78d7..ab135e3ed64 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -181,6 +181,7 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> { simple_fn, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None, + Some(instance), ) } sym::likely => { @@ -539,7 +540,7 @@ fn catch_unwind_intrinsic<'ll>( ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.call(try_func_ty, None, None, try_func, &[data], None); + bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. let ret_align = bx.tcx().data_layout.i32_align.abi; @@ -640,7 +641,7 @@ fn codegen_msvc_try<'ll>( let ptr_align = bx.tcx().data_layout.pointer_align.abi; let slot = bx.alloca(bx.type_ptr(), ptr_align); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -684,7 +685,7 @@ fn codegen_msvc_try<'ll>( let funclet = bx.catch_pad(cs, &[tydesc, flags, slot]); let ptr = bx.load(bx.type_ptr(), slot, ptr_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None); bx.catch_ret(&funclet, caught); // The flag value of 64 indicates a "catch-all". @@ -692,7 +693,7 @@ fn codegen_msvc_try<'ll>( let flags = bx.const_i32(64); let null = bx.const_null(bx.type_ptr()); let funclet = bx.catch_pad(cs, &[null, flags, null]); - bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, null], Some(&funclet), None); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -701,7 +702,7 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -750,7 +751,7 @@ fn codegen_wasm_try<'ll>( // } // let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], normal, catchswitch, None, None); bx.switch_to_block(normal); bx.ret(bx.const_i32(0)); @@ -766,7 +767,7 @@ fn codegen_wasm_try<'ll>( let _sel = bx.call_intrinsic("llvm.wasm.get.ehselector", &[funclet.cleanuppad()]); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet)); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], Some(&funclet), None); bx.catch_ret(&funclet, caught); bx.switch_to_block(caught); @@ -775,7 +776,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -818,7 +819,7 @@ fn codegen_gnu_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -836,13 +837,13 @@ fn codegen_gnu_try<'ll>( bx.add_clause(vals, tydesc); let ptr = bx.extract_value(vals, 0); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, ptr], None); + bx.call(catch_ty, None, None, catch_func, &[data, ptr], None, None); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -882,7 +883,7 @@ fn codegen_emcc_try<'ll>( let data = llvm::get_param(bx.llfn(), 1); let catch_func = llvm::get_param(bx.llfn(), 2); let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); - bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None); + bx.invoke(try_func_ty, None, None, try_func, &[data], then, catch, None, None); bx.switch_to_block(then); bx.ret(bx.const_i32(0)); @@ -920,13 +921,13 @@ fn codegen_emcc_try<'ll>( bx.store(is_rust_panic, catch_data_1, i8_align); let catch_ty = bx.type_func(&[bx.type_ptr(), bx.type_ptr()], bx.type_void()); - bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None); + bx.call(catch_ty, None, None, catch_func, &[data, catch_data], None, None); bx.ret(bx.const_i32(1)); }); // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). - let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None); + let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); let i32_align = bx.tcx().data_layout.i32_align.abi; bx.store(ret, dest, i32_align); } @@ -1439,6 +1440,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &args.iter().map(|arg| arg.immediate()).collect::<Vec<_>>(), None, + None, ); Ok(c) } @@ -1607,6 +1609,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[0].immediate()], None, + None, ); return Ok(v); } @@ -1706,6 +1709,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[1].immediate(), alignment, mask, args[2].immediate()], None, + None, ); return Ok(v); } @@ -1799,6 +1803,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[2].immediate(), args[1].immediate(), alignment, mask], None, + None, ); return Ok(v); } @@ -1904,6 +1909,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), args[1].immediate(), alignment, mask], None, + None, ); return Ok(v); } @@ -2352,11 +2358,12 @@ fn generic_simd_intrinsic<'ll, 'tcx>( f, &[args[0].immediate(), bx.const_int(bx.type_i1(), 0)], None, + None, )) } else { let fn_ty = bx.type_func(&[vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None)) + Ok(bx.call(fn_ty, None, None, f, &[args[0].immediate()], None, None)) }; } @@ -2409,7 +2416,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( let fn_ty = bx.type_func(&[vec_ty, vec_ty], vec_ty); let f = bx.declare_cfn(llvm_intrinsic, llvm::UnnamedAddr::No, fn_ty); - let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None); + let v = bx.call(fn_ty, None, None, f, &[lhs, rhs], None, None); return Ok(v); } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 13809ef72ec..f7f2bfca838 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -462,27 +462,34 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let ptr_ty = cx.type_ptr(); let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx); - let (start_fn, start_ty, args) = if let EntryFnType::Main { sigpipe } = entry_type { + let (start_fn, start_ty, args, instance) = if let EntryFnType::Main { sigpipe } = entry_type + { let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None); - let start_fn = cx.get_fn_addr(ty::Instance::expect_resolve( + let start_instance = ty::Instance::expect_resolve( cx.tcx(), ty::ParamEnv::reveal_all(), start_def_id, cx.tcx().mk_args(&[main_ret_ty.into()]), - )); + ); + let start_fn = cx.get_fn_addr(start_instance); let i8_ty = cx.type_i8(); let arg_sigpipe = bx.const_u8(sigpipe); let start_ty = cx.type_func(&[cx.val_ty(rust_main), isize_ty, ptr_ty, i8_ty], isize_ty); - (start_fn, start_ty, vec![rust_main, arg_argc, arg_argv, arg_sigpipe]) + ( + start_fn, + start_ty, + vec![rust_main, arg_argc, arg_argv, arg_sigpipe], + Some(start_instance), + ) } else { debug!("using user-defined start fn"); let start_ty = cx.type_func(&[isize_ty, ptr_ty], isize_ty); - (rust_main, start_ty, vec![arg_argc, arg_argv]) + (rust_main, start_ty, vec![arg_argc, arg_argv], None) }; - let result = bx.call(start_ty, None, None, start_fn, &args, None); + let result = bx.call(start_ty, None, None, start_fn, &args, None, instance); if cx.sess().target.os.contains("uefi") { bx.ret(result); } else { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index dcc27a4f0e5..8c668597a43 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -232,6 +232,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { ret_llbb, unwind_block, self.funclet(fx), + instance, ); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(invokeret); @@ -247,7 +248,8 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> { } MergingSucc::False } else { - let llret = bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx)); + let llret = + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, llargs, self.funclet(fx), instance); if fx.mir[self.bb].is_cleanup { bx.apply_attrs_to_cleanup_callsite(llret); } @@ -502,7 +504,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ty = location.ty(self.mir, bx.tcx()).ty; let ty = self.monomorphize(ty); let drop_fn = Instance::resolve_drop_in_place(bx.tcx(), ty); - let instance = drop_fn.clone(); if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { // we don't actually need to drop anything. @@ -518,7 +519,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args1 = [place.llval]; &args1[..] }; - let (drop_fn, fn_abi) = + let (drop_fn, fn_abi, drop_instance) = match ty.kind() { // FIXME(eddyb) perhaps move some of this logic into // `Instance::resolve_drop_in_place`? @@ -550,6 +551,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) .get_fn(bx, vtable, ty, fn_abi), fn_abi, + virtual_drop, ) } ty::Dynamic(_, _, ty::DynStar) => { @@ -592,9 +594,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_DROPINPLACE) .get_fn(bx, meta.immediate(), ty, fn_abi), fn_abi, + virtual_drop, ) } - _ => (bx.get_fn_addr(drop_fn), bx.fn_abi_of_instance(drop_fn, ty::List::empty())), + _ => ( + bx.get_fn_addr(drop_fn), + bx.fn_abi_of_instance(drop_fn, ty::List::empty()), + drop_fn, + ), }; helper.do_call( self, @@ -605,7 +612,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Some((ReturnDest::Nothing, target)), unwind, &[], - Some(instance), + Some(drop_instance), mergeable_succ, ) } @@ -1699,7 +1706,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { let fn_ty = bx.fn_decl_backend_type(fn_abi); - let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref()); + let llret = bx.call(fn_ty, None, Some(fn_abi), fn_ptr, &[], funclet.as_ref(), None); bx.apply_attrs_to_cleanup_callsite(llret); } diff --git a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs index ee70465966d..72187277228 100644 --- a/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/coverageinfo.rs @@ -1,12 +1,12 @@ use crate::traits::*; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::SourceScope; use super::FunctionCx; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - pub fn codegen_coverage(&self, bx: &mut Bx, coverage: &Coverage, scope: SourceScope) { + pub fn codegen_coverage(&self, bx: &mut Bx, kind: &CoverageKind, scope: SourceScope) { // Determine the instance that coverage data was originally generated for. let instance = if let Some(inlined) = scope.inlined_instance(&self.mir.source_scopes) { self.monomorphize(inlined) @@ -15,6 +15,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; // Handle the coverage info in a backend-specific way. - bx.add_coverage(instance, coverage); + bx.add_coverage(instance, kind); } } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 5532ff6e6a5..3e6cf0ece29 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -9,6 +9,7 @@ use crate::traits::*; use crate::MemFlags; use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_session::config::OptLevel; use rustc_span::{sym, Span}; use rustc_target::abi::{ call::{FnAbi, PassMode}, @@ -75,6 +76,29 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let name = bx.tcx().item_name(def_id); let name_str = name.as_str(); + // If we're swapping something that's *not* an `OperandValue::Ref`, + // then we can do it directly and avoid the alloca. + // Otherwise, we'll let the fallback MIR body take care of it. + if let sym::typed_swap = name { + let pointee_ty = fn_args.type_at(0); + let pointee_layout = bx.layout_of(pointee_ty); + if !bx.is_backend_ref(pointee_layout) + // But if we're not going to optimize, trying to use the fallback + // body just makes things worse, so don't bother. + || bx.sess().opts.optimize == OptLevel::No + // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary + // reinterpretation of values as (chunkable) byte arrays, and the loop in the + // block optimization in `ptr::swap_nonoverlapping` is hard to rewrite back + // into the (unoptimized) direct swapping implementation, so we disable it. + || bx.sess().target.arch == "spirv" + { + let x_place = PlaceRef::new_sized(args[0].immediate(), pointee_layout); + let y_place = PlaceRef::new_sized(args[1].immediate(), pointee_layout); + bx.typed_place_swap(x_place, y_place); + return Ok(()); + } + } + let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 15f2e0e56d8..0af84ff067a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -680,8 +680,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = layout.offset_of_subfield(bx.cx(), fields.iter()).bytes(); bx.cx().const_usize(val) } - mir::NullOp::UbCheck(_) => { - // In codegen, we want to check for language UB and library UB + mir::NullOp::UbChecks => { let val = bx.tcx().sess.opts.debug_assertions; bx.cx().const_bool(val) } @@ -710,7 +709,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } else { None }; - bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None) + bx.call(fn_ty, fn_attrs, Some(fn_abi), fn_ptr, &[], None, Some(instance)) } else { bx.get_static(def_id) }; diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs index ac7dfbb261d..2188eeae426 100644 --- a/compiler/rustc_codegen_ssa/src/mir/statement.rs +++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs @@ -64,8 +64,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { cg_indirect_place.storage_dead(bx); } } - mir::StatementKind::Coverage(box ref coverage) => { - self.codegen_coverage(bx, coverage, statement.source_info.scope); + mir::StatementKind::Coverage(ref kind) => { + self.codegen_coverage(bx, kind, statement.source_info.scope); } mir::StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(ref op)) => { if !matches!(bx.tcx().sess.opts.optimize, OptLevel::No | OptLevel::Less) { diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index e2e95cede60..c250cc26823 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -70,7 +70,15 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // (But we are in good company, this code is duplicated plenty of times.) let fn_ty = bx.fn_decl_backend_type(fn_abi); - bx.call(fn_ty, /* fn_attrs */ None, Some(fn_abi), llfn, &[msg.0, msg.1], None); + bx.call( + fn_ty, + /* fn_attrs */ None, + Some(fn_abi), + llfn, + &[msg.0, msg.1], + None, + None, + ); // This function does not return so we can now return whatever we want. let size = bx.const_usize(layout.size.bytes()); diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs index 36f37e3791b..6c8dcc5b690 100644 --- a/compiler/rustc_codegen_ssa/src/traits/builder.rs +++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs @@ -1,22 +1,24 @@ use super::abi::AbiBuilderMethods; use super::asm::AsmBuilderMethods; +use super::consts::ConstMethods; use super::coverageinfo::CoverageInfoBuilderMethods; use super::debuginfo::DebugInfoBuilderMethods; use super::intrinsic::IntrinsicCallMethods; use super::misc::MiscMethods; -use super::type_::{ArgAbiMethods, BaseTypeMethods}; +use super::type_::{ArgAbiMethods, BaseTypeMethods, LayoutTypeMethods}; use super::{HasCodegen, StaticBuilderMethods}; use crate::common::{ AtomicOrdering, AtomicRmwBinOp, IntPredicate, RealPredicate, SynchronizationScope, TypeKind, }; -use crate::mir::operand::OperandRef; +use crate::mir::operand::{OperandRef, OperandValue}; use crate::mir::place::PlaceRef; use crate::MemFlags; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{HasParamEnv, TyAndLayout}; -use rustc_middle::ty::Ty; +use rustc_middle::ty::{Instance, Ty}; +use rustc_session::config::OptLevel; use rustc_span::Span; use rustc_target::abi::call::FnAbi; use rustc_target::abi::{Abi, Align, Scalar, Size, WrappingRange}; @@ -80,6 +82,7 @@ pub trait BuilderMethods<'a, 'tcx>: then: Self::BasicBlock, catch: Self::BasicBlock, funclet: Option<&Self::Funclet>, + instance: Option<Instance<'tcx>>, ) -> Self::Value; fn unreachable(&mut self); @@ -267,6 +270,54 @@ pub trait BuilderMethods<'a, 'tcx>: flags: MemFlags, ); + /// *Typed* copy for non-overlapping places. + /// + /// Has a default implementation in terms of `memcpy`, but specific backends + /// can override to do something smarter if possible. + /// + /// (For example, typed load-stores with alias metadata.) + fn typed_place_copy( + &mut self, + dst: PlaceRef<'tcx, Self::Value>, + src: PlaceRef<'tcx, Self::Value>, + ) { + debug_assert!(src.llextra.is_none()); + debug_assert!(dst.llextra.is_none()); + debug_assert_eq!(dst.layout.size, src.layout.size); + if self.sess().opts.optimize == OptLevel::No && self.is_backend_immediate(dst.layout) { + // If we're not optimizing, the aliasing information from `memcpy` + // isn't useful, so just load-store the value for smaller code. + let temp = self.load_operand(src); + temp.val.store(self, dst); + } else if !dst.layout.is_zst() { + let bytes = self.const_usize(dst.layout.size.bytes()); + self.memcpy(dst.llval, dst.align, src.llval, src.align, bytes, MemFlags::empty()); + } + } + + /// *Typed* swap for non-overlapping places. + /// + /// Avoids `alloca`s for Immediates and ScalarPairs. + /// + /// FIXME: Maybe do something smarter for Ref types too? + /// For now, the `typed_swap` intrinsic just doesn't call this for those + /// cases (in non-debug), preferring the fallback body instead. + fn typed_place_swap( + &mut self, + left: PlaceRef<'tcx, Self::Value>, + right: PlaceRef<'tcx, Self::Value>, + ) { + let mut temp = self.load_operand(left); + if let OperandValue::Ref(..) = temp.val { + // The SSA value isn't stand-alone, so we need to copy it elsewhere + let alloca = PlaceRef::alloca(self, left.layout); + self.typed_place_copy(alloca, left); + temp = self.load_operand(alloca); + } + self.typed_place_copy(left, right); + temp.val.store(self, right); + } + fn select( &mut self, cond: Self::Value, @@ -339,6 +390,7 @@ pub trait BuilderMethods<'a, 'tcx>: llfn: Self::Value, args: &[Self::Value], funclet: Option<&Self::Funclet>, + instance: Option<Instance<'tcx>>, ) -> Self::Value; fn zext(&mut self, val: Self::Value, dest_ty: Self::Type) -> Self::Value; diff --git a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs index 7e8de0ddc5b..d1d813bd389 100644 --- a/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs +++ b/compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs @@ -1,5 +1,5 @@ use super::BackendTypes; -use rustc_middle::mir::Coverage; +use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::ty::Instance; pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { @@ -7,5 +7,5 @@ pub trait CoverageInfoBuilderMethods<'tcx>: BackendTypes { /// /// This can potentially be a no-op in backends that don't support /// coverage instrumentation. - fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage); + fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind); } diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs index 72cce43a3fa..555833759eb 100644 --- a/compiler/rustc_codegen_ssa/src/traits/type_.rs +++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs @@ -120,6 +120,20 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> { immediate: bool, ) -> Self::Type; + /// A type that produces an [`OperandValue::Ref`] when loaded. + /// + /// AKA one that's not a ZST, not `is_backend_immediate`, and + /// not `is_backend_scalar_pair`. For such a type, a + /// [`load_operand`] doesn't actually `load` anything. + /// + /// [`OperandValue::Ref`]: crate::mir::operand::OperandValue::Ref + /// [`load_operand`]: super::BuilderMethods::load_operand + fn is_backend_ref(&self, layout: TyAndLayout<'tcx>) -> bool { + !(layout.is_zst() + || self.is_backend_immediate(layout) + || self.is_backend_scalar_pair(layout)) + } + /// A type that can be used in a [`super::BuilderMethods::load`] + /// [`super::BuilderMethods::store`] pair to implement a *typed* copy, /// such as a MIR `*_0 = *_1`. diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs index 16bd0296247..5a1c7cc4209 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -3,7 +3,7 @@ use either::{Left, Right}; use rustc_hir::def::DefKind; use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo}; use rustc_middle::mir::{self, ConstAlloc, ConstValue}; -use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::query::TyCtxtAt; use rustc_middle::traits::Reveal; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; @@ -243,24 +243,6 @@ pub(crate) fn turn_into_const_value<'tcx>( op_to_const(&ecx, &mplace.into(), /* for diagnostics */ false) } -/// Computes the tag (if any) for a given type and variant. -#[instrument(skip(tcx), level = "debug")] -pub fn tag_for_variant_provider<'tcx>( - tcx: TyCtxt<'tcx>, - (ty, variant_index): (Ty<'tcx>, abi::VariantIdx), -) -> Option<ty::ScalarInt> { - assert!(ty.is_enum()); - - let ecx = InterpCx::new( - tcx, - ty.default_span(tcx), - ty::ParamEnv::reveal_all(), - crate::const_eval::DummyMachine, - ); - - ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) -} - #[instrument(skip(tcx), level = "debug")] pub fn eval_to_const_value_raw_provider<'tcx>( tcx: TyCtxt<'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index b768c429070..8efc67bcb0c 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -2,10 +2,11 @@ use rustc_middle::mir; use rustc_middle::mir::interpret::InterpErrorInfo; -use rustc_middle::query::TyCtxtAt; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::query::{Key, TyCtxtAt}; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_target::abi::VariantIdx; -use crate::interpret::format_interp_error; +use crate::interpret::{format_interp_error, InterpCx}; mod dummy_machine; mod error; @@ -77,3 +78,21 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>( Some(mir::DestructuredConstant { variant, fields }) } + +/// Computes the tag (if any) for a given type and variant. +#[instrument(skip(tcx), level = "debug")] +pub fn tag_for_variant_provider<'tcx>( + tcx: TyCtxt<'tcx>, + (ty, variant_index): (Ty<'tcx>, VariantIdx), +) -> Option<ty::ScalarInt> { + assert!(ty.is_enum()); + + let ecx = InterpCx::new( + tcx, + ty.default_span(tcx), + ty::ParamEnv::reveal_all(), + crate::const_eval::DummyMachine, + ); + + ecx.tag_for_variant(ty, variant_index).unwrap().map(|(tag, _tag_field)| tag) +} diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs index 40469c6632c..704f597cfdb 100644 --- a/compiler/rustc_const_eval/src/interpret/discriminant.rs +++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs @@ -38,7 +38,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } None => { // No need to write the tag here, because an untagged variant is - // implicitly encoded. For `Niche`-optimized enums, it's by + // implicitly encoded. For `Niche`-optimized enums, this works by // simply by having a value that is outside the niche variants. // But what if the data stored here does not actually encode // this variant? That would be bad! So let's double-check... @@ -227,8 +227,10 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(discr_value, discr_layout)) } - /// Computes the tag value and its field number (if any) of a given variant - /// of type `ty`. + /// Computes how to write the tag of a given variant of enum `ty`: + /// - `None` means that nothing needs to be done as the variant is encoded implicitly + /// - `Some((val, field_idx))` means that the given integer value needs to be stored at the + /// given field index. pub(crate) fn tag_for_variant( &self, ty: Ty<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs index a84ef4ce08e..a8478f721c7 100644 --- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs +++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs @@ -21,8 +21,8 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_target::abi::Size; use super::{ - util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, MPlaceTy, Machine, OpTy, - Pointer, + memory::MemoryKind, util::ensure_monomorphic_enough, CheckInAllocMsg, ImmTy, InterpCx, + MPlaceTy, Machine, OpTy, Pointer, }; use crate::fluent_generated as fluent; @@ -414,6 +414,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let result = self.raw_eq_intrinsic(&args[0], &args[1])?; self.write_scalar(result, dest)?; } + sym::typed_swap => { + self.typed_swap_intrinsic(&args[0], &args[1])?; + } sym::vtable_size => { let ptr = self.read_pointer(&args[0])?; @@ -607,6 +610,24 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.mem_copy(src, dst, size, nonoverlapping) } + /// Does a *typed* swap of `*left` and `*right`. + fn typed_swap_intrinsic( + &mut self, + left: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + right: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, + ) -> InterpResult<'tcx> { + let left = self.deref_pointer(left)?; + let right = self.deref_pointer(right)?; + debug_assert_eq!(left.layout, right.layout); + let kind = MemoryKind::Stack; + let temp = self.allocate(left.layout, kind)?; + self.copy_op(&left, &temp)?; + self.copy_op(&right, &left)?; + self.copy_op(&temp, &right)?; + self.deallocate_ptr(temp.ptr(), None, kind)?; + Ok(()) + } + pub(crate) fn write_bytes_intrinsic( &mut self, dst: &OpTy<'tcx, <M as Machine<'mir, 'tcx>>::Provenance>, diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index 54bac70da38..9114ffff6fd 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -258,17 +258,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let val = layout.offset_of_subfield(self, fields.iter()).bytes(); Scalar::from_target_usize(val, self) } - mir::NullOp::UbCheck(kind) => { - // We want to enable checks for library UB, because the interpreter doesn't - // know about those on its own. - // But we want to disable checks for language UB, because the interpreter - // has its own better checks for that. - let should_check = match kind { - mir::UbKind::LibraryUb => self.tcx.sess.opts.debug_assertions, - mir::UbKind::LanguageUb => false, - }; - Scalar::from_bool(should_check) - } + mir::NullOp::UbChecks => Scalar::from_bool(self.tcx.sess.opts.debug_assertions), }; self.write_scalar(val, &dest)?; } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs index a93e8138aa4..da8e28d0298 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs @@ -558,7 +558,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { Rvalue::Cast(_, _, _) => {} Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _, ) => {} Rvalue::ShallowInitBox(_, _) => {} diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 4bc49f90607..08e3e42a82e 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -85,7 +85,7 @@ impl<'tcx> MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) { cfg_checker.fail(location, msg); } @@ -346,8 +346,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { self.fail(location, format!("explicit `{kind:?}` is forbidden")); } } - StatementKind::Coverage(coverage) => { - let kind = &coverage.kind; + StatementKind::Coverage(kind) => { if self.mir_phase >= MirPhase::Analysis(AnalysisPhase::PostCleanup) && let CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. } = kind { @@ -541,19 +540,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// A faster version of the validation pass that only checks those things which may break when /// instantiating any generic parameters. +/// +/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before +/// `optimized_mir` is available. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, + caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + let mut type_checker = + TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, + caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, @@ -705,8 +710,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } &ty::Coroutine(def_id, args) => { let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.body.source.def_id() { - self.body + // If we're currently validating an inlined copy of this body, + // then it will no longer be parameterized over the original + // args of the coroutine. Otherwise, we prefer to use this body + // since we may be in the process of computing this MIR in the + // first place. + let gen_body = if def_id == self.caller_body.source.def_id() { + self.caller_body } else { self.tcx.optimized_mir(def_id) }; @@ -1168,7 +1178,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { Rvalue::Repeat(_, _) | Rvalue::ThreadLocalRef(_) | Rvalue::AddressOf(_, _) - | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbCheck(_), _) + | Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::UbChecks, _) | Rvalue::Discriminant(_) => {} } self.super_rvalue(rvalue, location); diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index fceccb7e9b6..bd8e78bda26 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -541,6 +541,7 @@ pub struct SilentEmitter { pub fallback_bundle: LazyFallbackBundle, pub fatal_dcx: DiagCtxt, pub fatal_note: Option<String>, + pub emit_fatal_diagnostic: bool, } impl Translate for SilentEmitter { @@ -561,7 +562,7 @@ impl Emitter for SilentEmitter { } fn emit_diagnostic(&mut self, mut diag: DiagInner) { - if diag.level == Level::Fatal { + if self.emit_fatal_diagnostic && diag.level == Level::Fatal { if let Some(fatal_note) = &self.fatal_note { diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new()); } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 238bc63ec58..7b40954e735 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -612,12 +612,18 @@ impl DiagCtxt { Self { inner: Lock::new(DiagCtxtInner::new(emitter)) } } - pub fn make_silent(&mut self, fallback_bundle: LazyFallbackBundle, fatal_note: Option<String>) { + pub fn make_silent( + &mut self, + fallback_bundle: LazyFallbackBundle, + fatal_note: Option<String>, + emit_fatal_diagnostic: bool, + ) { self.wrap_emitter(|old_dcx| { Box::new(emitter::SilentEmitter { fallback_bundle, fatal_dcx: DiagCtxt { inner: Lock::new(old_dcx) }, fatal_note, + emit_fatal_diagnostic, }) }); } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 162ec16fabc..cc0ab05d422 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1220,7 +1220,7 @@ pub struct Stmt<'hir> { #[derive(Debug, Clone, Copy, HashStable_Generic)] pub enum StmtKind<'hir> { /// A local (`let`) binding. - Let(&'hir Local<'hir>), + Let(&'hir LetStmt<'hir>), /// An item binding. Item(ItemId), @@ -1234,7 +1234,7 @@ pub enum StmtKind<'hir> { /// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`). #[derive(Debug, Clone, Copy, HashStable_Generic)] -pub struct Local<'hir> { +pub struct LetStmt<'hir> { pub pat: &'hir Pat<'hir>, /// Type annotation, if any (otherwise the type will be inferred). pub ty: Option<&'hir Ty<'hir>>, @@ -1264,7 +1264,7 @@ pub struct Arm<'hir> { pub body: &'hir Expr<'hir>, } -/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a [`Local`]), occurring in an `if-let` +/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a [`LetStmt`]), occurring in an `if-let` /// or `let-else`, evaluating to a boolean. Typically the pattern is refutable. /// /// In an `if let`, imagine it as `if (let <pat> = <expr>) { ... }`; in a let-else, it is part of @@ -1861,7 +1861,7 @@ pub enum ExprKind<'hir> { DropTemps(&'hir Expr<'hir>), /// A `let $pat = $expr` expression. /// - /// These are not `Local` and only occur as expressions. + /// These are not [`LetStmt`] and only occur as expressions. /// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`. Let(&'hir LetExpr<'hir>), /// An `if` block, with an optional else block. @@ -3529,7 +3529,7 @@ pub enum Node<'hir> { PatField(&'hir PatField<'hir>), Arm(&'hir Arm<'hir>), Block(&'hir Block<'hir>), - Local(&'hir Local<'hir>), + LetStmt(&'hir LetStmt<'hir>), /// `Ctor` refers to the constructor of an enum variant or struct. Only tuple or unit variants /// with synthesized constructors. Ctor(&'hir VariantData<'hir>), @@ -3585,7 +3585,7 @@ impl<'hir> Node<'hir> { | Node::Ctor(..) | Node::Pat(..) | Node::Arm(..) - | Node::Local(..) + | Node::LetStmt(..) | Node::Crate(..) | Node::Ty(..) | Node::TraitRef(..) @@ -3757,7 +3757,7 @@ impl<'hir> Node<'hir> { expect_pat_field, &'hir PatField<'hir>, Node::PatField(n), n; expect_arm, &'hir Arm<'hir>, Node::Arm(n), n; expect_block, &'hir Block<'hir>, Node::Block(n), n; - expect_local, &'hir Local<'hir>, Node::Local(n), n; + expect_let_stmt, &'hir LetStmt<'hir>, Node::LetStmt(n), n; expect_ctor, &'hir VariantData<'hir>, Node::Ctor(n), n; expect_lifetime, &'hir Lifetime, Node::Lifetime(n), n; expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n; @@ -3787,7 +3787,7 @@ mod size_asserts { static_assert_size!(ImplItemKind<'_>, 40); static_assert_size!(Item<'_>, 88); static_assert_size!(ItemKind<'_>, 56); - static_assert_size!(Local<'_>, 64); + static_assert_size!(LetStmt<'_>, 64); static_assert_size!(Param<'_>, 32); static_assert_size!(Pat<'_>, 72); static_assert_size!(Path<'_>, 40); diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 468c7fd5c73..8c44f21a57b 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -320,7 +320,7 @@ pub trait Visitor<'v>: Sized { fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) -> Self::Result { walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v Local<'v>) -> Self::Result { + fn visit_local(&mut self, l: &'v LetStmt<'v>) -> Self::Result { walk_local(self, l) } fn visit_block(&mut self, b: &'v Block<'v>) -> Self::Result { @@ -606,7 +606,7 @@ pub fn walk_foreign_item<'v, V: Visitor<'v>>( V::Result::output() } -pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) -> V::Result { +pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v LetStmt<'v>) -> V::Result { // Intentionally visiting the expr first - the initialization expr // dominates the local's definition. visit_opt!(visitor, visit_expr, local.init); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 8fb6569aa14..50f88eb970a 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -639,10 +639,9 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( } } - if !unnormalized_trait_sig.output().references_error() { - debug_assert!( - !collector.types.is_empty(), - "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`" + if !unnormalized_trait_sig.output().references_error() && collector.types.is_empty() { + tcx.dcx().delayed_bug( + "expect >0 RPITITs in call to `collect_return_position_impl_trait_in_trait_tys`", ); } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 0b526a8c977..f482ae4f5fa 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -127,8 +127,7 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) - | sym::variant_count | sym::is_val_statically_known | sym::ptr_mask - | sym::check_language_ub - | sym::check_library_ub + | sym::ub_checks | sym::fadd_algebraic | sym::fsub_algebraic | sym::fmul_algebraic @@ -484,6 +483,8 @@ pub fn check_intrinsic_type( (1, 0, vec![Ty::new_mut_ptr(tcx, param(0)), param(0)], Ty::new_unit(tcx)) } + sym::typed_swap => (1, 1, vec![Ty::new_mut_ptr(tcx, param(0)); 2], Ty::new_unit(tcx)), + sym::discriminant_value => { let assoc_items = tcx.associated_item_def_ids( tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), @@ -569,7 +570,7 @@ pub fn check_intrinsic_type( (0, 0, vec![Ty::new_imm_ptr(tcx, Ty::new_unit(tcx))], tcx.types.usize) } - sym::check_language_ub | sym::check_library_ub => (0, 1, Vec::new(), tcx.types.bool), + sym::ub_checks => (0, 1, Vec::new(), tcx.types.bool), sym::simd_eq | sym::simd_ne diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs index 6cb15708a42..df4db3ec3fb 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs @@ -6,7 +6,9 @@ use rustc_session::lint; use rustc_span::def_id::LocalDefId; use rustc_span::Symbol; use rustc_target::abi::FieldIdx; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; +use rustc_target::asm::{ + InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, +}; pub struct InlineAsmCtxt<'a, 'tcx> { tcx: TyCtxt<'tcx>, @@ -251,8 +253,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // Check whether a modifier is suggested for using this type. - if let Some((suggested_modifier, suggested_result)) = - reg_class.suggest_modifier(asm_arch, asm_ty) + if let Some(ModifierInfo { + modifier: suggested_modifier, + result: suggested_result, + size: suggested_size, + }) = reg_class.suggest_modifier(asm_arch, asm_ty) { // Search for any use of this operand without a modifier and emit // the suggestion for them. @@ -266,8 +271,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } if !spans.is_empty() { - let (default_modifier, default_result) = - reg_class.default_modifier(asm_arch).unwrap(); + let ModifierInfo { + modifier: default_modifier, + result: default_result, + size: default_size, + } = reg_class.default_modifier(asm_arch).unwrap(); self.tcx.node_span_lint( lint::builtin::ASM_SUB_REGISTER, expr.hir_id, @@ -276,10 +284,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { |lint| { lint.span_label(expr.span, "for this argument"); lint.help(format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", + "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}` (for {suggested_size}-bit values)", )); lint.help(format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", + "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}` (for {default_size}-bit values)", )); }, ); diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index e84bce92fc5..dcabac6d780 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; +use rustc_hir::{Arm, Block, Expr, LetStmt, Pat, PatKind, Stmt}; use rustc_index::Idx; use rustc_middle::middle::region::*; use rustc_middle::ty::TyCtxt; @@ -123,7 +123,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h for (i, statement) in blk.stmts.iter().enumerate() { match statement.kind { - hir::StmtKind::Let(hir::Local { els: Some(els), .. }) => { + hir::StmtKind::Let(LetStmt { els: Some(els), .. }) => { // Let-else has a special lexical structure for variables. // First we take a checkpoint of the current scope context here. let mut prev_cx = visitor.cx; @@ -855,7 +855,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { resolve_expr(self, ex); } - fn visit_local(&mut self, l: &'tcx Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) { resolve_local(self, Some(l.pat), l.init) } } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 71fdcc76a70..36f59b4ac2e 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -113,7 +113,7 @@ impl<'a> State<'a> { // `hir_map` to reconstruct their full structure for pretty // printing. Node::Ctor(..) => panic!("cannot print isolated Ctor"), - Node::Local(a) => self.print_local_decl(a), + Node::LetStmt(a) => self.print_local_decl(a), Node::Crate(..) => panic!("cannot print Crate"), Node::WhereBoundPredicate(pred) => { self.print_formal_generic_params(pred.bound_generic_params); @@ -1544,7 +1544,7 @@ impl<'a> State<'a> { self.end() } - fn print_local_decl(&mut self, loc: &hir::Local<'_>) { + fn print_local_decl(&mut self, loc: &hir::LetStmt<'_>) { self.print_pat(loc.pat); if let Some(ty) = loc.ty { self.word_space(":"); diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs index 1805e9fbb98..9b7db9e4c8e 100644 --- a/compiler/rustc_hir_typeck/src/_match.rs +++ b/compiler/rustc_hir_typeck/src/_match.rs @@ -408,7 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node { + if let hir::Node::LetStmt(hir::LetStmt { ty: Some(_), pat, .. }) = node { return Some((pat.span, "expected because of this assignment".to_string())); } None diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 67ff412651c..564de4ab9e7 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -299,8 +299,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return false; }; let (init_ty_hir_id, init) = match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init), - hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)), + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init, .. }) => (ty.hir_id, *init), + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => (init.hir_id, Some(*init)), _ => return false, }; let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { @@ -678,7 +678,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { error: Option<TypeError<'tcx>>, ) { match (self.tcx.parent_hir_node(expr.hir_id), error) { - (hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. }), _) + (hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), init: Some(init), .. }), _) if init.hir_id == expr.hir_id => { // Point at `let` assignment type. @@ -724,11 +724,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { primary_span = pat.span; secondary_span = pat.span; match self.tcx.parent_hir_node(pat.hir_id) { - hir::Node::Local(hir::Local { ty: Some(ty), .. }) => { + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { primary_span = ty.span; post_message = " type"; } - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { primary_span = init.span; post_message = " value"; } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index f632e495295..f38f04fce43 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1451,7 +1451,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); let Some(( _, - hir::Node::Local(hir::Local { ty: Some(ty), .. }) + hir::Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }), )) = parent_node else { diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index f3d523efd68..3b6accb92ae 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -371,7 +371,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { match stmt.kind { - hir::StmtKind::Let(hir::Local { pat, init: Some(expr), els, .. }) => { + hir::StmtKind::Let(hir::LetStmt { pat, init: Some(expr), els, .. }) => { self.walk_local(expr, pat, *els, |_| {}) } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 3a0a3de968d..ebc5e11a561 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1601,7 +1601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + pub fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) { self.check_decl(local.into()); if local.pat.is_never_pattern() { self.diverges.set(Diverges::Always { @@ -1789,7 +1789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { [ hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { source: hir::LocalSource::AssignDesugar(_), .. diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 810735d5424..442bfd75746 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -317,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); expr_id = parent_id; } - Node::Local(local) => { + Node::LetStmt(local) => { if let Some(mut ty) = local.ty { while let Some(index) = tuple_indexes.pop() { match ty.kind { @@ -1348,7 +1348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // ++++++++++ // since the user probably just misunderstood how `let else` // and `&&` work together. - if let Some((_, hir::Node::Local(local))) = cond_parent + if let Some((_, hir::Node::LetStmt(local))) = cond_parent && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath @@ -1748,7 +1748,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match self.tcx.parent_hir_node(*hir_id) { // foo.clone() - hir::Node::Local(hir::Local { init: Some(init), .. }) => { + hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) => { self.note_type_is_not_clone_inner_expr(init) } // When `expr` is more complex like a tuple @@ -1757,7 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { kind: hir::PatKind::Tuple(pats, ..), .. }) => { - let hir::Node::Local(hir::Local { init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*pat_hir_id) else { return expr; @@ -1791,7 +1791,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let hir::Path { segments: [_], res: crate::Res::Local(binding), .. } = call_expr_path && let hir::Node::Pat(hir::Pat { hir_id, .. }) = self.tcx.hir_node(*binding) - && let hir::Node::Local(hir::Local { init: Some(init), .. }) = + && let hir::Node::LetStmt(hir::LetStmt { init: Some(init), .. }) = self.tcx.parent_hir_node(*hir_id) && let Expr { kind: hir::ExprKind::Closure(hir::Closure { body: body_id, .. }), @@ -3147,7 +3147,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs index 8fddccab5bf..1fa799dcec7 100644 --- a/compiler/rustc_hir_typeck/src/gather_locals.rs +++ b/compiler/rustc_hir_typeck/src/gather_locals.rs @@ -29,7 +29,7 @@ impl<'a> DeclOrigin<'a> { } } -/// A declaration is an abstraction of [hir::Local] and [hir::LetExpr]. +/// A declaration is an abstraction of [hir::LetStmt] and [hir::LetExpr]. /// /// It must have a hir_id, as this is how we connect gather_locals to the check functions. pub(super) struct Declaration<'a> { @@ -41,9 +41,9 @@ pub(super) struct Declaration<'a> { pub origin: DeclOrigin<'a>, } -impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { - fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; +impl<'a> From<&'a hir::LetStmt<'a>> for Declaration<'a> { + fn from(local: &'a hir::LetStmt<'a>) -> Self { + let hir::LetStmt { hir_id, pat, ty, span, init, els, source: _ } = *local; Declaration { hir_id, pat, ty, span, init, origin: DeclOrigin::LocalDecl { els } } } } @@ -120,7 +120,7 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.declare(local.into()); intravisit::walk_local(self, local) } diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 62664f27662..0dcde0cdecb 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2163,7 +2163,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (filename, parent_node) { ( FileName::Real(_), - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { source: hir::LocalSource::Normal, ty, .. @@ -2218,7 +2218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { impl<'v> Visitor<'v> for LetVisitor { type Result = ControlFlow<Option<&'v hir::Expr<'v>>>; fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(&hir::Local { pat, init, .. }) = ex.kind + if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind && let Binding(_, _, ident, ..) = pat.kind && ident.name == self.ident_name { diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 5ddd0f8be25..7ecd380ebeb 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -744,7 +744,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let ident_kind = match binding_parent { hir::Node::Param(_) => "parameter", - hir::Node::Local(_) => "variable", + hir::Node::LetStmt(_) => "variable", hir::Node::Arm(_) => "binding", // Provide diagnostics only if the parent pattern is struct-like, diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index f88988ec8a3..e489b431e81 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -217,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bug!(); }; for stmt in block.stmts { - let hir::StmtKind::Let(hir::Local { + let hir::StmtKind::Let(hir::LetStmt { init: Some(init), source: hir::LocalSource::AsyncFn, pat, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 0740a3fd3e9..f4516b684c3 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -351,7 +351,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { intravisit::walk_pat(self, p); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { intravisit::walk_local(self, l); let var_ty = self.fcx.local_ty(l.span, l.hir_id); let var_ty = self.resolve(var_ty, &l.span); diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index e44a6ae3b3f..521c65c6009 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -169,7 +169,7 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_meant_str_literal = if you meant to write a string literal, use double quotes infer_mismatched_static_lifetime = incompatible lifetime on type infer_more_targeted = {$has_param_name -> [true] `{$param_name}` diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index a3cf0d8e520..d0b1f2848ff 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1339,15 +1339,12 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[suggestion( - infer_meant_str_literal, - code = "\"{code}\"", - applicability = "machine-applicable" - )] + #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { - #[primary_span] - span: Span, - code: String, + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, }, #[suggestion( infer_consider_specifying_length, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 7ce6c35f8c3..82634f7308d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2079,16 +2079,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - span, - code: escape_literal(code), - }) - } - } + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }) } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` @@ -2140,7 +2134,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // the same span as the error and the type is specified. if let hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { + hir::StmtKind::Let(hir::LetStmt { init: Some(hir::Expr { span: init_span, .. }), ty: Some(array_ty), .. diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index ce9001ccb1c..f89ed256a08 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -11,7 +11,7 @@ use rustc_hir::def::Res; use rustc_hir::def::{CtorOf, DefKind, Namespace}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, LetStmt, LocalSource}; use rustc_middle::hir::nested_filter; use rustc_middle::infer::unify_key::{ ConstVariableOrigin, ConstVariableOriginKind, ConstVariableValue, @@ -1122,7 +1122,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { self.tecx.tcx.hir() } - fn visit_local(&mut self, local: &'tcx Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); if let Some(ty) = self.opt_node_type(local.hir_id) { diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index 9081fbaa2dc..e220c4d02a2 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -2,7 +2,7 @@ use crate::infer::error_reporting::hir::Path; use core::ops::ControlFlow; use hir::def::CtorKind; use hir::intravisit::{walk_expr, walk_stmt, Visitor}; -use hir::{Local, QPath}; +use hir::{LetStmt, QPath}; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; @@ -321,7 +321,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { && let Some(expr) = block.expr && let hir::ExprKind::Path(QPath::Resolved(_, Path { res, .. })) = expr.kind && let Res::Local(local) = res - && let Node::Local(Local { init: Some(init), .. }) = self.tcx.parent_hir_node(*local) + && let Node::LetStmt(LetStmt { init: Some(init), .. }) = + self.tcx.parent_hir_node(*local) { fn collect_blocks<'hir>(expr: &hir::Expr<'hir>, blocks: &mut Vec<&hir::Block<'hir>>) { match expr.kind { @@ -585,7 +586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result { - if let hir::StmtKind::Let(hir::Local { + if let hir::StmtKind::Let(LetStmt { span, pat: hir::Pat { .. }, ty: None, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 8ba14d37982..656c7ffae19 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -48,6 +48,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg { let psess = ParseSess::with_silent_emitter( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this error occurred on the command line: `--cfg={s}`"), + true, ); let filename = FileName::cfg_spec_source_code(&s); @@ -111,6 +112,7 @@ pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg { let psess = ParseSess::with_silent_emitter( vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this error occurred on the command line: `--check-cfg={s}`"), + true, ); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index aba7f95487e..d173c3ac032 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -46,7 +46,7 @@ impl<'a> Cursor<'a> { /// If requested position doesn't exist, `EOF_CHAR` is returned. /// However, getting `EOF_CHAR` doesn't always mean actual end of file, /// it should be checked with `is_eof` method. - pub(crate) fn first(&self) -> char { + pub fn first(&self) -> char { // `.next()` optimizes better than `.nth(0)` self.chars.clone().next().unwrap_or(EOF_CHAR) } @@ -59,6 +59,15 @@ impl<'a> Cursor<'a> { iter.next().unwrap_or(EOF_CHAR) } + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + /// Checks if there is nothing more to consume. pub(crate) fn is_eof(&self) -> bool { self.chars.as_str().is_empty() diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 8c0e5b17fd8..64ef30039a8 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -937,7 +937,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, _ => None, } { @@ -982,7 +982,7 @@ impl<'tcx> LateContext<'tcx> { } && let Some(init) = match parent_node { hir::Node::Expr(expr) => Some(expr), - hir::Node::Local(hir::Local { init, .. }) => *init, + hir::Node::LetStmt(hir::LetStmt { init, .. }) => *init, hir::Node::Item(item) => match item.kind { hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => { Some(self.tcx.hir().body(body_id).value) diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 506716e39a1..384bd353d75 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -238,7 +238,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.with_lint_attrs(l.hir_id, |cx| { lint_callback!(cx, check_local, l); hir_visit::walk_local(cx, l); diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index de642f373b5..9d4d75a646a 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -105,7 +105,7 @@ const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { #[allow(rustc::untranslatable_diagnostic)] // FIXME: make this translatable - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if matches!(local.source, rustc_hir::LocalSource::AsyncFn) { return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 26fc3f20b2c..5bb2942ad4b 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -354,7 +354,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } @@ -428,7 +428,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<' intravisit::walk_variant(self, v); } - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, l: &'tcx hir::LetStmt<'tcx>) { self.add_id(l.hir_id); intravisit::walk_local(self, l); } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 508f3e1ec31..3e93cc0be6a 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -15,7 +15,7 @@ macro_rules! late_lint_methods { fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>); fn check_item(a: &'tcx rustc_hir::Item<'tcx>); fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>); - fn check_local(a: &'tcx rustc_hir::Local<'tcx>); + fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>); fn check_block(a: &'tcx rustc_hir::Block<'tcx>); fn check_block_post(a: &'tcx rustc_hir::Block<'tcx>); fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs index b74430d8fa0..6222adf58ae 100644 --- a/compiler/rustc_lint/src/unit_bindings.rs +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -46,7 +46,7 @@ declare_lint! { declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]); impl<'tcx> LateLintPass<'tcx> for UnitBindings { - fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::LetStmt<'tcx>) { // Suppress warning if user: // - explicitly ascribes a type to the pattern // - explicitly wrote `let pat = ();` diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index b544bc8a782..784fd4b3a3b 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -389,6 +389,15 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { None } + // The `dependency` type is determined by the command line arguments(`--extern`) and + // `private_dep`. However, sometimes the directly dependent crate is not specified by + // `--extern`, in this case, `private-dep` is none during loading. This is equivalent to the + // scenario where the command parameter is set to `public-dependency` + fn is_private_dep(&self, name: &str, private_dep: Option<bool>) -> bool { + self.sess.opts.externs.get(name).map_or(private_dep.unwrap_or(false), |e| e.is_private_dep) + && private_dep.unwrap_or(true) + } + fn register_crate( &mut self, host_lib: Option<Library>, @@ -404,14 +413,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { let Library { source, metadata } = lib; let crate_root = metadata.get_root(); let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash()); - - let private_dep = self - .sess - .opts - .externs - .get(name.as_str()) - .map_or(private_dep.unwrap_or(false), |e| e.is_private_dep) - && private_dep.unwrap_or(true); + let private_dep = self.is_private_dep(name.as_str(), private_dep); // Claim this crate number and cache it let cnum = self.cstore.intern_stable_crate_id(&crate_root)?; @@ -601,14 +603,17 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { match result { (LoadResult::Previous(cnum), None) => { + // When `private_dep` is none, it indicates the directly dependent crate. If it is + // not specified by `--extern` on command line parameters, it may be + // `private-dependency` when `register_crate` is called for the first time. Then it must be updated to + // `public-dependency` here. + let private_dep = self.is_private_dep(name.as_str(), private_dep); let data = self.cstore.get_crate_data_mut(cnum); if data.is_proc_macro_crate() { dep_kind = CrateDepKind::MacrosOnly; } data.set_dep_kind(cmp::max(data.dep_kind(), dep_kind)); - if let Some(private_dep) = private_dep { - data.update_and_private_dep(private_dep); - } + data.update_and_private_dep(private_dep); Ok(cnum) } (LoadResult::Loaded(library), host_library) => { diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 1aabd296641..b69a295f010 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -211,6 +211,7 @@ provide! { tcx, def_id, other, cdata, generics_of => { table } inferred_outlives_of => { table_defaulted_array } super_predicates_of => { table } + implied_predicates_of => { table } type_of => { table } type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) } variances_of => { table } @@ -276,18 +277,6 @@ provide! { tcx, def_id, other, cdata, .map(|lazy| lazy.decode((cdata, tcx))) .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys"))) } - implied_predicates_of => { - cdata - .root - .tables - .implied_predicates_of - .get(cdata, def_id.index) - .map(|lazy| lazy.decode((cdata, tcx))) - .unwrap_or_else(|| { - debug_assert_eq!(tcx.def_kind(def_id), DefKind::Trait); - tcx.super_predicates_of(def_id) - }) - } associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 42724f7dd2b..61060038b50 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1435,6 +1435,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { if let DefKind::Trait = def_kind { record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id)); record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id)); + record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id)); let module_children = self.tcx.module_children_local(local_id); record_array!(self.tables.module_children_non_reexports[def_id] <- diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index a1bcee84d4c..53cb05198cd 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -567,7 +567,7 @@ impl<'hir> Map<'hir> { } // Ignore `return`s on the first iteration Node::Expr(Expr { kind: ExprKind::Loop(..) | ExprKind::Ret(..), .. }) - | Node::Local(_) => { + | Node::LetStmt(_) => { return None; } _ => {} @@ -906,7 +906,7 @@ impl<'hir> Map<'hir> { Node::Lifetime(lifetime) => lifetime.ident.span, Node::GenericParam(param) => param.span, Node::Infer(i) => i.span, - Node::Local(local) => local.span, + Node::LetStmt(local) => local.span, Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, @@ -1163,7 +1163,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Arm(_) => node_str("arm"), Node::Block(_) => node_str("block"), Node::Infer(_) => node_str("infer"), - Node::Local(_) => node_str("local"), + Node::LetStmt(_) => node_str("local"), Node::Ctor(ctor) => format!( "{id} (ctor {})", ctor.ctor_def_id().map_or("<missing path>".into(), |def_id| path_str(def_id)), diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 48edbde68e5..e4dce2bdc9e 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -796,7 +796,7 @@ impl<'tcx> Body<'tcx> { } match rvalue { - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => { + Rvalue::NullaryOp(NullOp::UbChecks, _) => { Some((tcx.sess.opts.debug_assertions as u128, targets)) } Rvalue::Use(Operand::Constant(constant)) => { diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 94751c44761..f0499cf344f 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -13,7 +13,7 @@ use rustc_middle::mir::interpret::{ Provenance, }; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{self, *}; +use rustc_middle::mir::*; use rustc_target::abi::Size; const INDENT: &str = " "; @@ -711,7 +711,7 @@ impl Debug for Statement<'_> { AscribeUserType(box (ref place, ref c_ty), ref variance) => { write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})") } - Coverage(box mir::Coverage { ref kind }) => write!(fmt, "Coverage::{kind:?}"), + Coverage(ref kind) => write!(fmt, "Coverage::{kind:?}"), Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"), ConstEvalCounter => write!(fmt, "ConstEvalCounter"), Nop => write!(fmt, "nop"), @@ -944,7 +944,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { NullOp::SizeOf => write!(fmt, "SizeOf({t})"), NullOp::AlignOf => write!(fmt, "AlignOf({t})"), NullOp::OffsetOf(fields) => write!(fmt, "OffsetOf({t}, {fields:?})"), - NullOp::UbCheck(kind) => write!(fmt, "UbCheck({kind:?})"), + NullOp::UbChecks => write!(fmt, "UbChecks()"), } } ThreadLocalRef(did) => ty::tls::with(|tcx| { diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 752f5845afb..12bf8efd73a 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -373,7 +373,7 @@ pub enum StatementKind<'tcx> { /// /// Interpreters and codegen backends that don't support coverage instrumentation /// can usually treat this as a no-op. - Coverage(Box<Coverage>), + Coverage(CoverageKind), /// Denotes a call to an intrinsic that does not require an unwind path and always returns. /// This avoids adding a new block and a terminator for simple intrinsics. @@ -519,12 +519,6 @@ pub enum FakeReadCause { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] #[derive(TypeFoldable, TypeVisitable)] -pub struct Coverage { - pub kind: CoverageKind, -} - -#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)] -#[derive(TypeFoldable, TypeVisitable)] pub struct CopyNonOverlapping<'tcx> { pub src: Operand<'tcx>, pub dst: Operand<'tcx>, @@ -1367,16 +1361,9 @@ pub enum NullOp<'tcx> { AlignOf, /// Returns the offset of a field OffsetOf(&'tcx List<(VariantIdx, FieldIdx)>), - /// Returns whether we want to check for library UB or language UB at monomorphization time. - /// Both kinds of UB evaluate to `true` in codegen, and only library UB evalutes to `true` in - /// const-eval/Miri, because the interpreter has its own better checks for language UB. - UbCheck(UbKind), -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)] -pub enum UbKind { - LanguageUb, - LibraryUb, + /// Returns whether we want to check for UB. + /// This returns the value of `cfg!(debug_assertions)` at monomorphization time. + UbChecks, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -1465,5 +1452,6 @@ mod size_asserts { static_assert_size!(Place<'_>, 16); static_assert_size!(PlaceElem<'_>, 24); static_assert_size!(Rvalue<'_>, 40); + static_assert_size!(StatementKind<'_>, 16); // tidy-alphabetical-end } diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index ac41b6c5732..56a0a623397 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -194,7 +194,7 @@ impl<'tcx> Rvalue<'tcx> { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { tcx.types.usize } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => tcx.types.bool, + Rvalue::NullaryOp(NullOp::UbChecks, _) => tcx.types.bool, Rvalue::Aggregate(ref ak, ref ops) => match **ak { AggregateKind::Array(ty) => Ty::new_array(tcx, ty, ops.len() as u64), AggregateKind::Tuple => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index be960669ff4..3835bd371d9 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -156,10 +156,10 @@ macro_rules! make_mir_visitor { fn visit_coverage( &mut self, - coverage: & $($mutability)? Coverage, + kind: & $($mutability)? coverage::CoverageKind, location: Location, ) { - self.super_coverage(coverage, location); + self.super_coverage(kind, location); } fn visit_retag( @@ -803,7 +803,7 @@ macro_rules! make_mir_visitor { } fn super_coverage(&mut self, - _coverage: & $($mutability)? Coverage, + _kind: & $($mutability)? coverage::CoverageKind, _location: Location) { } diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index 2bd0e289731..18e45291e9a 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -107,9 +107,7 @@ impl<'tcx> CFG<'tcx> { /// This results in more accurate coverage reports for certain kinds of /// syntax (e.g. `continue` or `if !`) that would otherwise not appear in MIR. pub(crate) fn push_coverage_span_marker(&mut self, block: BasicBlock, source_info: SourceInfo) { - let kind = StatementKind::Coverage(Box::new(Coverage { - kind: coverage::CoverageKind::SpanMarker, - })); + let kind = StatementKind::Coverage(coverage::CoverageKind::SpanMarker); let stmt = Statement { source_info, kind }; self.push(block, stmt); } diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs index 0b8ec234dda..ab0043906b1 100644 --- a/compiler/rustc_mir_build/src/build/coverageinfo.rs +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -127,9 +127,7 @@ impl Builder<'_, '_> { let marker_statement = mir::Statement { source_info, - kind: mir::StatementKind::Coverage(Box::new(mir::Coverage { - kind: CoverageKind::BlockMarker { id }, - })), + kind: mir::StatementKind::Coverage(CoverageKind::BlockMarker { id }), }; self.cfg.push(block, marker_statement); diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index 3ca0eb4acd4..e73d945e0bb 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -433,7 +433,7 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> { | Rvalue::Discriminant(..) | Rvalue::Len(..) | Rvalue::NullaryOp( - NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbCheck(_), + NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..) | NullOp::UbChecks, _, ) => {} } diff --git a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs index aaf2035fc21..da82f8de781 100644 --- a/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs +++ b/compiler/rustc_mir_transform/src/cleanup_post_borrowck.rs @@ -18,7 +18,7 @@ use crate::MirPass; use rustc_middle::mir::coverage::CoverageKind; -use rustc_middle::mir::{Body, BorrowKind, Coverage, Rvalue, StatementKind, TerminatorKind}; +use rustc_middle::mir::{Body, BorrowKind, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::TyCtxt; pub struct CleanupPostBorrowck; @@ -30,12 +30,11 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { match statement.kind { StatementKind::AscribeUserType(..) | StatementKind::Assign(box (_, Rvalue::Ref(_, BorrowKind::Fake, _))) - | StatementKind::Coverage(box Coverage { + | StatementKind::Coverage( // These kinds of coverage statements are markers inserted during // MIR building, and are not needed after InstrumentCoverage. - kind: CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, - .. - }) + CoverageKind::BlockMarker { .. } | CoverageKind::SpanMarker { .. }, + ) | StatementKind::FakeRead(..) => statement.make_nop(), _ => (), } diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 000b96ee801..e0bbd582d88 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -64,12 +64,9 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut by_move_body = body.clone(); MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - by_move_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - }, - promoted: None, - }; + by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + coroutine_def_id: coroutine_def_id.to_def_id(), + }); body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); } } diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 83189c6a50a..ae3b1a3d1af 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -15,7 +15,7 @@ use crate::MirPass; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{ - self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, + self, BasicBlock, BasicBlockData, SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; @@ -230,10 +230,7 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb debug!(" injecting statement {counter_kind:?} for {bb:?}"); let data = &mut mir_body[bb]; let source_info = data.terminator().source_info; - let statement = Statement { - source_info, - kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind })), - }; + let statement = Statement { source_info, kind: StatementKind::Coverage(counter_kind) }; data.statements.insert(0, statement); } diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 1de7b6f66a7..b5dd9dcc7b4 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,7 +1,7 @@ use rustc_data_structures::captures::Captures; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{CounterId, CoverageKind}; -use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo, Statement, StatementKind}; +use rustc_middle::mir::{Body, CoverageIdsInfo, Statement, StatementKind}; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -54,7 +54,7 @@ fn coverage_ids_info<'tcx>( let mir_body = tcx.instance_mir(instance_def); let max_counter_id = all_coverage_in_mir_body(mir_body) - .filter_map(|coverage| match coverage.kind { + .filter_map(|kind| match *kind { CoverageKind::CounterIncrement { id } => Some(id), _ => None, }) @@ -66,12 +66,10 @@ fn coverage_ids_info<'tcx>( fn all_coverage_in_mir_body<'a, 'tcx>( body: &'a Body<'tcx>, -) -> impl Iterator<Item = &'a Coverage> + Captures<'tcx> { +) -> impl Iterator<Item = &'a CoverageKind> + Captures<'tcx> { body.basic_blocks.iter().flat_map(|bb_data| &bb_data.statements).filter_map(|statement| { match statement.kind { - StatementKind::Coverage(box ref coverage) if !is_inlined(body, statement) => { - Some(coverage) - } + StatementKind::Coverage(ref kind) if !is_inlined(body, statement) => Some(kind), _ => None, } }) diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs index 3f6a4156044..adb0c9f1929 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -187,9 +187,7 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { // for their parent `BasicBlock`. StatementKind::StorageLive(_) | StatementKind::StorageDead(_) - // Ignore `ConstEvalCounter`s | StatementKind::ConstEvalCounter - // Ignore `Nop`s | StatementKind::Nop => None, // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` @@ -211,30 +209,28 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> { StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None, // Retain spans from most other statements. - StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding` + StatementKind::FakeRead(_) | StatementKind::Intrinsic(..) - | StatementKind::Coverage(box mir::Coverage { + | StatementKind::Coverage( // The purpose of `SpanMarker` is to be matched and accepted here. - kind: CoverageKind::SpanMarker - }) + CoverageKind::SpanMarker, + ) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) | StatementKind::Retag(_, _) | StatementKind::PlaceMention(..) - | StatementKind::AscribeUserType(_, _) => { - Some(statement.source_info.span) - } + | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), - StatementKind::Coverage(box mir::Coverage { - // Block markers are used for branch coverage, so ignore them here. - kind: CoverageKind::BlockMarker {..} - }) => None, + // Block markers are used for branch coverage, so ignore them here. + StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None, - StatementKind::Coverage(box mir::Coverage { - // These coverage statements should not exist prior to coverage instrumentation. - kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } - }) => bug!("Unexpected coverage statement found during coverage instrumentation: {statement:?}"), + // These coverage statements should not exist prior to coverage instrumentation. + StatementKind::Coverage( + CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. }, + ) => bug!( + "Unexpected coverage statement found during coverage instrumentation: {statement:?}" + ), } } @@ -382,9 +378,7 @@ pub(super) fn extract_branch_mappings( // Fill out the mapping from block marker IDs to their enclosing blocks. for (bb, data) in mir_body.basic_blocks.iter_enumerated() { for statement in &data.statements { - if let StatementKind::Coverage(coverage) = &statement.kind - && let CoverageKind::BlockMarker { id } = coverage.kind - { + if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind { block_markers[id] = Some(bb); } } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index fdc81c0a99e..59d6d89cf1f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -487,7 +487,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { NullOp::OffsetOf(fields) => { layout.offset_of_subfield(&self.ecx, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; let usize_layout = self.ecx.layout_of(self.tcx.types.usize).unwrap(); let imm = ImmTy::try_from_uint(val, usize_layout)?; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 4ec76eec3a9..78c0615b165 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -213,6 +213,7 @@ impl<'tcx> Inliner<'tcx> { MirPhase::Runtime(RuntimePhase::Optimized), self.param_env, &callee_body, + &caller_body, ) .is_empty() { diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index 6b13725b386..a20958e74df 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -639,7 +639,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { NullOp::OffsetOf(fields) => { op_layout.offset_of_subfield(self, fields.iter()).bytes() } - NullOp::UbCheck(_) => return None, + NullOp::UbChecks => return None, }; ImmTy::from_scalar(Scalar::from_target_usize(val, self), layout).into() } diff --git a/compiler/rustc_mir_transform/src/lower_intrinsics.rs b/compiler/rustc_mir_transform/src/lower_intrinsics.rs index 1bab240ef50..7d4c1b9c21a 100644 --- a/compiler/rustc_mir_transform/src/lower_intrinsics.rs +++ b/compiler/rustc_mir_transform/src/lower_intrinsics.rs @@ -20,30 +20,13 @@ impl<'tcx> MirPass<'tcx> for LowerIntrinsics { sym::unreachable => { terminator.kind = TerminatorKind::Unreachable; } - sym::check_language_ub => { + sym::ub_checks => { let target = target.unwrap(); block.statements.push(Statement { source_info: terminator.source_info, kind: StatementKind::Assign(Box::new(( *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LanguageUb), - tcx.types.bool, - ), - ))), - }); - terminator.kind = TerminatorKind::Goto { target }; - } - sym::check_library_ub => { - let target = target.unwrap(); - block.statements.push(Statement { - source_info: terminator.source_info, - kind: StatementKind::Assign(Box::new(( - *destination, - Rvalue::NullaryOp( - NullOp::UbCheck(UbKind::LibraryUb), - tcx.types.bool, - ), + Rvalue::NullaryOp(NullOp::UbChecks, tcx.types.bool), ))), }); terminator.kind = TerminatorKind::Goto { target }; diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs index 202ea571985..2951897ebd6 100644 --- a/compiler/rustc_mir_transform/src/promote_consts.rs +++ b/compiler/rustc_mir_transform/src/promote_consts.rs @@ -446,7 +446,7 @@ impl<'tcx> Validator<'_, 'tcx> { NullOp::SizeOf => {} NullOp::AlignOf => {} NullOp::OffsetOf(_) => {} - NullOp::UbCheck(_) => {} + NullOp::UbChecks => {} }, Rvalue::ShallowInitBox(_, _) => return Err(Unpromotable), diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index bd23b90fc31..16d8453ea24 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -239,7 +239,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, @@ -277,7 +277,6 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> } } } - ty::ReError(_) => return r, }; let existing_bound_var = match self.canonicalize_mode { diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e1d26f090e0..aa735f3de1f 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -568,7 +568,7 @@ parse_more_than_one_char = character literal may only contain one codepoint .remove_non = consider removing the non-printing characters .use_double_quotes = if you meant to write a {$is_byte -> [true] byte string - *[false] `str` + *[false] string } literal, use double quotes parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -833,6 +833,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 5cad3a568ee..20ebfc6691b 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1987,6 +1987,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] @@ -2198,12 +2209,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df..63b2b47630b 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option<Span>, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a string literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -669,15 +698,33 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { + let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + if self.cursor.first() == '\'' + && let Some(start) = self.last_lifetime + && self.cursor.third() != '\'' + { + // An "unclosed `char`" error will be emitted already, silence redundant error. + silence = true; + Some(errors::UnknownPrefixSugg::MeantStr { + start, + end: self.mk_sp(self.pos, self.pos + BytePos(1)), + }) + } else { + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } } else { None }; - self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); + let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; + if silence { + self.dcx().create_err(err).delay_as_bug(); + } else { + self.dcx().emit_err(err); + } } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd7..fa242a32a18 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() || full_lit_span.is_empty() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs index d0dba938b3b..e6e52648d6f 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -264,7 +264,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { hir_visit::walk_foreign_item(self, i) } - fn visit_local(&mut self, l: &'v hir::Local<'v>) { + fn visit_local(&mut self, l: &'v hir::LetStmt<'v>) { self.record("Local", Id::Node(l.hir_id), l); hir_visit::walk_local(self, l) } diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index f0c3f7a385d..125084f4750 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -342,7 +342,7 @@ impl<'tcx> IrMaps<'tcx> { } impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.add_from_pat(local.pat); if local.els.is_some() { self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id)); @@ -1350,7 +1350,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { // Checking for error conditions impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> { - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { self.check_unused_vars_in_pat(local.pat, None, None, |spans, hir_id, ln, var| { if local.init.is_some() { self.warn_about_dead_assign(spans, hir_id, ln, var); diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index c73fdd59609..41d63407418 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1209,7 +1209,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> { intravisit::walk_pat(self, pattern); } - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { + fn visit_local(&mut self, local: &'tcx hir::LetStmt<'tcx>) { if let Some(init) = local.init { if self.check_expr_pat_type(init.hir_id, init.span) { // Do not report duplicate errors for `let x = y`. diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs index 2553df33cc7..93839fa1186 100644 --- a/compiler/rustc_session/src/code_stats.rs +++ b/compiler/rustc_session/src/code_stats.rs @@ -44,6 +44,10 @@ pub struct FieldInfo { pub offset: u64, pub size: u64, pub align: u64, + /// Name of the type of this field. + /// Present only if the creator thought that this would be important for identifying the field, + /// typically because the field name is uninformative. + pub type_name: Option<Symbol>, } #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] @@ -192,7 +196,7 @@ impl CodeStats { fields.sort_by_key(|f| (f.offset, f.size)); for field in fields { - let FieldInfo { kind, ref name, offset, size, align } = field; + let FieldInfo { kind, ref name, offset, size, align, type_name } = field; if offset > min_offset { let pad = offset - min_offset; @@ -201,21 +205,27 @@ impl CodeStats { if offset < min_offset { // If this happens it's probably a union. - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ offset: {offset} bytes, \ alignment: {align} bytes" ); } else if info.packed || offset == min_offset { - println!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); + print!("print-type-size {indent}{kind} `.{name}`: {size} bytes"); } else { // Include field alignment in output only if it caused padding injection - println!( + print!( "print-type-size {indent}{kind} `.{name}`: {size} bytes, \ alignment: {align} bytes" ); } + if let Some(type_name) = type_name { + println!(", type: {type_name}"); + } else { + println!(); + } + min_offset = offset + size; } } diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs index 5434bbe0b98..f6053f43fbd 100644 --- a/compiler/rustc_session/src/parse.rs +++ b/compiler/rustc_session/src/parse.rs @@ -269,7 +269,11 @@ impl ParseSess { } } - pub fn with_silent_emitter(locale_resources: Vec<&'static str>, fatal_note: String) -> Self { + pub fn with_silent_emitter( + locale_resources: Vec<&'static str>, + fatal_note: String, + emit_fatal_diagnostic: bool, + ) -> Self { let fallback_bundle = fallback_fluent_bundle(locale_resources, false); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let emitter = Box::new(HumanEmitter::new( @@ -281,6 +285,7 @@ impl ParseSess { fallback_bundle, fatal_dcx, fatal_note: Some(fatal_note), + emit_fatal_diagnostic, })) .disable_warnings(); ParseSess::with_dcx(dcx, sm) diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index c0876adf905..b6a722da602 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -251,19 +251,13 @@ impl<'tcx> Stable<'tcx> for mir::NullOp<'tcx> { type T = stable_mir::mir::NullOp; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { use rustc_middle::mir::NullOp::*; - use rustc_middle::mir::UbKind; match self { SizeOf => stable_mir::mir::NullOp::SizeOf, AlignOf => stable_mir::mir::NullOp::AlignOf, OffsetOf(indices) => stable_mir::mir::NullOp::OffsetOf( indices.iter().map(|idx| idx.stable(tables)).collect(), ), - UbCheck(UbKind::LanguageUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LanguageUb) - } - UbCheck(UbKind::LibraryUb) => { - stable_mir::mir::NullOp::UbCheck(stable_mir::mir::UbKind::LibraryUb) - } + UbChecks => stable_mir::mir::NullOp::UbChecks, } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b6c07e8737f..73fcd2a76df 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -518,8 +518,6 @@ symbols! { cfi, cfi_encoding, char, - check_language_ub, - check_library_ub, client, clippy, clobber_abi, @@ -1836,6 +1834,7 @@ symbols! { type_macros, type_name, type_privacy_lints, + typed_swap, u128, u128_legacy_const_max, u128_legacy_const_min, @@ -1866,6 +1865,7 @@ symbols! { u8_legacy_fn_max_value, u8_legacy_fn_min_value, u8_legacy_mod, + ub_checks, unaligned_volatile_load, unaligned_volatile_store, unboxed_closures, diff --git a/compiler/rustc_symbol_mangling/src/typeid.rs b/compiler/rustc_symbol_mangling/src/typeid.rs index e8763e49e62..3bf564a4a16 100644 --- a/compiler/rustc_symbol_mangling/src/typeid.rs +++ b/compiler/rustc_symbol_mangling/src/typeid.rs @@ -36,7 +36,7 @@ pub fn typeid_for_instance<'tcx>( instance: &Instance<'tcx>, options: TypeIdOptions, ) -> String { - typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options) + typeid_itanium_cxx_abi::typeid_for_instance(tcx, *instance, options) } /// Returns a KCFI type metadata identifier for the specified FnAbi. @@ -61,6 +61,6 @@ pub fn kcfi_typeid_for_instance<'tcx>( // A KCFI type metadata identifier is a 32-bit constant produced by taking the lower half of the // xxHash64 of the type metadata identifier. (See llvm/llvm-project@cff5bef.) let mut hash: XxHash64 = Default::default(); - hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, instance, options).as_bytes()); + hash.write(typeid_itanium_cxx_abi::typeid_for_instance(tcx, *instance, options).as_bytes()); hash.finish() as u32 } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 07a382d161d..367fec0e8fc 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -178,14 +178,14 @@ fn encode_fnsig<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("encode_fnsig: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_sig.output(), transform_ty_options); + let ty = transform_ty(tcx, fn_sig.output(), &mut Vec::new(), transform_ty_options); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); // Encode the parameter types let tys = fn_sig.inputs(); if !tys.is_empty() { for ty in tys { - let ty = transform_ty(tcx, *ty, transform_ty_options); + let ty = transform_ty(tcx, *ty, &mut Vec::new(), transform_ty_options); s.push_str(&encode_ty(tcx, ty, dict, encode_ty_options)); } @@ -767,11 +767,12 @@ fn transform_predicates<'tcx>( fn transform_args<'tcx>( tcx: TyCtxt<'tcx>, args: GenericArgsRef<'tcx>, + parents: &mut Vec<Ty<'tcx>>, options: TransformTyOptions, ) -> GenericArgsRef<'tcx> { let args = args.iter().map(|arg| match arg.unpack() { GenericArgKind::Type(ty) if ty.is_c_void(tcx) => Ty::new_unit(tcx).into(), - GenericArgKind::Type(ty) => transform_ty(tcx, ty, options).into(), + GenericArgKind::Type(ty) => transform_ty(tcx, ty, parents, options).into(), _ => arg, }); tcx.mk_args_from_iter(args) @@ -781,9 +782,12 @@ fn transform_args<'tcx>( // c_void types into unit types unconditionally, generalizes pointers if // TransformTyOptions::GENERALIZE_POINTERS option is set, and normalizes integers if // TransformTyOptions::NORMALIZE_INTEGERS option is set. -fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptions) -> Ty<'tcx> { - let mut ty = ty; - +fn transform_ty<'tcx>( + tcx: TyCtxt<'tcx>, + mut ty: Ty<'tcx>, + parents: &mut Vec<Ty<'tcx>>, + options: TransformTyOptions, +) -> Ty<'tcx> { match ty.kind() { ty::Float(..) | ty::Str | ty::Never | ty::Foreign(..) | ty::CoroutineWitness(..) => {} @@ -843,17 +847,20 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio _ if ty.is_unit() => {} ty::Tuple(tys) => { - ty = Ty::new_tup_from_iter(tcx, tys.iter().map(|ty| transform_ty(tcx, ty, options))); + ty = Ty::new_tup_from_iter( + tcx, + tys.iter().map(|ty| transform_ty(tcx, ty, parents, options)), + ); } ty::Array(ty0, len) => { let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); - ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len); + ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, parents, options), len); } ty::Slice(ty0) => { - ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, options)); + ty = Ty::new_slice(tcx, transform_ty(tcx, *ty0, parents, options)); } ty::Adt(adt_def, args) => { @@ -862,7 +869,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } else if options.contains(TransformTyOptions::GENERALIZE_REPR_C) && adt_def.repr().c() { ty = Ty::new_adt(tcx, *adt_def, ty::List::empty()); - } else if adt_def.repr().transparent() && adt_def.is_struct() { + } else if adt_def.repr().transparent() && adt_def.is_struct() && !parents.contains(&ty) + { // Don't transform repr(transparent) types with an user-defined CFI encoding to // preserve the user-defined CFI encoding. if let Some(_) = tcx.get_attr(adt_def.did(), sym::cfi_encoding) { @@ -881,38 +889,48 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio // Generalize any repr(transparent) user-defined type that is either a pointer // or reference, and either references itself or any other type that contains or // references itself, to avoid a reference cycle. + + // If the self reference is not through a pointer, for example, due + // to using `PhantomData`, need to skip normalizing it if we hit it again. + parents.push(ty); if ty0.is_any_ptr() && ty0.contains(ty) { ty = transform_ty( tcx, ty0, + parents, options | TransformTyOptions::GENERALIZE_POINTERS, ); } else { - ty = transform_ty(tcx, ty0, options); + ty = transform_ty(tcx, ty0, parents, options); } + parents.pop(); } else { // Transform repr(transparent) types without non-ZST field into () ty = Ty::new_unit(tcx); } } else { - ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, options)); + ty = Ty::new_adt(tcx, *adt_def, transform_args(tcx, args, parents, options)); } } ty::FnDef(def_id, args) => { - ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, options)); + ty = Ty::new_fn_def(tcx, *def_id, transform_args(tcx, args, parents, options)); } ty::Closure(def_id, args) => { - ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, options)); + ty = Ty::new_closure(tcx, *def_id, transform_args(tcx, args, parents, options)); } ty::CoroutineClosure(def_id, args) => { - ty = Ty::new_coroutine_closure(tcx, *def_id, transform_args(tcx, args, options)); + ty = Ty::new_coroutine_closure( + tcx, + *def_id, + transform_args(tcx, args, parents, options), + ); } ty::Coroutine(def_id, args) => { - ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, options)); + ty = Ty::new_coroutine(tcx, *def_id, transform_args(tcx, args, parents, options)); } ty::Ref(region, ty0, ..) => { @@ -924,9 +942,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } else { if ty.is_mutable_ptr() { - ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + ty = Ty::new_mut_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options)); } else { - ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, options)); + ty = Ty::new_imm_ref(tcx, *region, transform_ty(tcx, *ty0, parents, options)); } } } @@ -940,9 +958,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } } else { if ty.is_mutable_ptr() { - ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, options)); + ty = Ty::new_mut_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options)); } else { - ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, options)); + ty = Ty::new_imm_ptr(tcx, transform_ty(tcx, *ptr_ty, parents, options)); } } } @@ -955,9 +973,9 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio .skip_binder() .inputs() .iter() - .map(|ty| transform_ty(tcx, *ty, options)) + .map(|ty| transform_ty(tcx, *ty, parents, options)) .collect(); - let output = transform_ty(tcx, fn_sig.skip_binder().output(), options); + let output = transform_ty(tcx, fn_sig.skip_binder().output(), parents, options); ty = Ty::new_fn_ptr( tcx, ty::Binder::bind_with_vars( @@ -987,6 +1005,7 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio ty = transform_ty( tcx, tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty), + parents, options, ); } @@ -1037,7 +1056,7 @@ pub fn typeid_for_fnabi<'tcx>( // Encode the return type let transform_ty_options = TransformTyOptions::from_bits(options.bits()) .unwrap_or_else(|| bug!("typeid_for_fnabi: invalid option(s) `{:?}`", options.bits())); - let ty = transform_ty(tcx, fn_abi.ret.layout.ty, transform_ty_options); + let ty = transform_ty(tcx, fn_abi.ret.layout.ty, &mut Vec::new(), transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); // Encode the parameter types @@ -1049,7 +1068,7 @@ pub fn typeid_for_fnabi<'tcx>( let mut pushed_arg = false; for arg in fn_abi.args.iter().filter(|arg| arg.mode != PassMode::Ignore) { pushed_arg = true; - let ty = transform_ty(tcx, arg.layout.ty, transform_ty_options); + let ty = transform_ty(tcx, arg.layout.ty, &mut Vec::new(), transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } if !pushed_arg { @@ -1062,7 +1081,8 @@ pub fn typeid_for_fnabi<'tcx>( if fn_abi.args[n].mode == PassMode::Ignore { continue; } - let ty = transform_ty(tcx, fn_abi.args[n].layout.ty, transform_ty_options); + let ty = + transform_ty(tcx, fn_abi.args[n].layout.ty, &mut Vec::new(), transform_ty_options); typeid.push_str(&encode_ty(tcx, ty, &mut dict, encode_ty_options)); } @@ -1088,11 +1108,15 @@ pub fn typeid_for_fnabi<'tcx>( /// vendor extended type qualifiers and types for Rust types that are not used at the FFI boundary. pub fn typeid_for_instance<'tcx>( tcx: TyCtxt<'tcx>, - instance: &Instance<'tcx>, + mut instance: Instance<'tcx>, options: TypeIdOptions, ) -> String { + if matches!(instance.def, ty::InstanceDef::Virtual(..)) { + instance.args = strip_receiver_auto(tcx, instance.args) + } + let fn_abi = tcx - .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((*instance, ty::List::empty()))) + .fn_abi_of_instance(tcx.param_env(instance.def_id()).and((instance, ty::List::empty()))) .unwrap_or_else(|instance| { bug!("typeid_for_instance: couldn't get fn_abi of instance {:?}", instance) }); @@ -1138,3 +1162,23 @@ pub fn typeid_for_instance<'tcx>( typeid_for_fnabi(tcx, fn_abi, options) } + +fn strip_receiver_auto<'tcx>( + tcx: TyCtxt<'tcx>, + args: ty::GenericArgsRef<'tcx>, +) -> ty::GenericArgsRef<'tcx> { + let ty = args.type_at(0); + let ty::Dynamic(preds, lifetime, kind) = ty.kind() else { + bug!("Tried to strip auto traits from non-dynamic type {ty}"); + }; + let filtered_preds = + if preds.principal().is_some() { + tcx.mk_poly_existential_predicates_from_iter(preds.into_iter().filter(|pred| { + !matches!(pred.skip_binder(), ty::ExistentialPredicate::AutoTrait(..)) + })) + } else { + ty::List::empty() + }; + let new_rcvr = Ty::new_dynamic(tcx, filtered_preds, *lifetime, *kind); + tcx.mk_args_trait(new_rcvr, args.into_iter().skip(1)) +} diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index 97132311a5c..70528c1222c 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -27,32 +27,28 @@ impl AArch64InlineAsmRegClass { None } - pub fn suggest_modifier( - self, - _arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, _arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::reg => match ty.size().bits() { 64 => None, - _ => Some(('w', "w0")), + _ => Some(('w', "w0", 32).into()), }, Self::vreg | Self::vreg_low16 => match ty.size().bits() { - 8 => Some(('b', "b0")), - 16 => Some(('h', "h0")), - 32 => Some(('s', "s0")), - 64 => Some(('d', "d0")), - 128 => Some(('q', "q0")), + 8 => Some(('b', "b0", 8).into()), + 16 => Some(('h', "h0", 16).into()), + 32 => Some(('s', "s0", 32).into()), + 64 => Some(('d', "d0", 64).into()), + 128 => Some(('q', "q0", 128).into()), _ => None, }, Self::preg => None, } } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { match self { - Self::reg => Some(('x', "x0")), - Self::vreg | Self::vreg_low16 => Some(('v', "v0")), + Self::reg => Some(('x', "x0", 64).into()), + Self::vreg | Self::vreg_low16 => Some(('v', "v0", 128).into()), Self::preg => None, } } diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index 514e30ae020..f56dbac708b 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -35,11 +35,11 @@ impl ArmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/avr.rs b/compiler/rustc_target/src/asm/avr.rs index 9a96a61f5b2..eab38a4a4f4 100644 --- a/compiler/rustc_target/src/asm/avr.rs +++ b/compiler/rustc_target/src/asm/avr.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -29,11 +29,11 @@ impl AvrInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index 3b03766a089..9bc94274675 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl BpfInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/csky.rs b/compiler/rustc_target/src/asm/csky.rs index db3d8106040..64607ee4b81 100644 --- a/compiler/rustc_target/src/asm/csky.rs +++ b/compiler/rustc_target/src/asm/csky.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl CSKYInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/hexagon.rs b/compiler/rustc_target/src/asm/hexagon.rs index d20270ac9e9..19da7b80848 100644 --- a/compiler/rustc_target/src/asm/hexagon.rs +++ b/compiler/rustc_target/src/asm/hexagon.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl HexagonInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/loongarch.rs b/compiler/rustc_target/src/asm/loongarch.rs index 9d1a4f3eeea..15d0f54ce3b 100644 --- a/compiler/rustc_target/src/asm/loongarch.rs +++ b/compiler/rustc_target/src/asm/loongarch.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl LoongArchInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/m68k.rs b/compiler/rustc_target/src/asm/m68k.rs index 8c857550cf2..ac94dcc03dc 100644 --- a/compiler/rustc_target/src/asm/m68k.rs +++ b/compiler/rustc_target/src/asm/m68k.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl M68kInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/mips.rs b/compiler/rustc_target/src/asm/mips.rs index 4e7c2eb1bf8..0ac1a43ae18 100644 --- a/compiler/rustc_target/src/asm/mips.rs +++ b/compiler/rustc_target/src/asm/mips.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -23,11 +23,11 @@ impl MipsInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index a11884bea26..2e04dca98c5 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -6,6 +6,18 @@ use rustc_span::Symbol; use std::fmt; use std::str::FromStr; +pub struct ModifierInfo { + pub modifier: char, + pub result: &'static str, + pub size: u64, +} + +impl From<(char, &'static str, u64)> for ModifierInfo { + fn from((modifier, result, size): (char, &'static str, u64)) -> Self { + Self { modifier, result, size } + } +} + macro_rules! def_reg_class { ($arch:ident $arch_regclass:ident { $( @@ -512,11 +524,7 @@ impl InlineAsmRegClass { /// Such suggestions are useful if a type smaller than the full register /// size is used and a modifier can be used to point to the subregister of /// the correct size. - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::X86(r) => r.suggest_modifier(arch, ty), Self::Arm(r) => r.suggest_modifier(arch, ty), @@ -545,7 +553,7 @@ impl InlineAsmRegClass { /// This is only needed when the register class can suggest a modifier, so /// that the user can be shown how to get the default behavior without a /// warning. - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<ModifierInfo> { match self { Self::X86(r) => r.default_modifier(arch), Self::Arm(r) => r.default_modifier(arch), diff --git a/compiler/rustc_target/src/asm/msp430.rs b/compiler/rustc_target/src/asm/msp430.rs index a27d6390a72..439f3ba0b57 100644 --- a/compiler/rustc_target/src/asm/msp430.rs +++ b/compiler/rustc_target/src/asm/msp430.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -22,11 +22,11 @@ impl Msp430InlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/nvptx.rs b/compiler/rustc_target/src/asm/nvptx.rs index 8e1e91e7c5f..57aa50fceb8 100644 --- a/compiler/rustc_target/src/asm/nvptx.rs +++ b/compiler/rustc_target/src/asm/nvptx.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -23,11 +23,11 @@ impl NvptxInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/powerpc.rs b/compiler/rustc_target/src/asm/powerpc.rs index d3ccb30350a..4e8cbe34de9 100644 --- a/compiler/rustc_target/src/asm/powerpc.rs +++ b/compiler/rustc_target/src/asm/powerpc.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -26,11 +26,11 @@ impl PowerPCInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index dea6d50fe2b..2505d36f5b8 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -26,11 +26,11 @@ impl RiscVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs index b8afeb824d8..6bc668454b1 100644 --- a/compiler/rustc_target/src/asm/s390x.rs +++ b/compiler/rustc_target/src/asm/s390x.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; use std::fmt; @@ -24,11 +24,11 @@ impl S390xInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/spirv.rs b/compiler/rustc_target/src/asm/spirv.rs index 31073da10b2..d13a6131f9a 100644 --- a/compiler/rustc_target/src/asm/spirv.rs +++ b/compiler/rustc_target/src/asm/spirv.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl SpirVInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/wasm.rs b/compiler/rustc_target/src/asm/wasm.rs index f095b7c6e11..eb0b23ef43d 100644 --- a/compiler/rustc_target/src/asm/wasm.rs +++ b/compiler/rustc_target/src/asm/wasm.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -21,11 +21,11 @@ impl WasmInlineAsmRegClass { self, _arch: InlineAsmArch, _ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + ) -> Option<ModifierInfo> { None } - pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> { None } diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index 3902dac7ff6..3b5da8806cc 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,4 +1,4 @@ -use super::{InlineAsmArch, InlineAsmType}; +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; use crate::spec::{RelocModel, Target}; use rustc_data_structures::fx::FxIndexSet; use rustc_macros::HashStable_Generic; @@ -53,32 +53,28 @@ impl X86InlineAsmRegClass { } } - pub fn suggest_modifier( - self, - arch: InlineAsmArch, - ty: InlineAsmType, - ) -> Option<(char, &'static str)> { + pub fn suggest_modifier(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option<ModifierInfo> { match self { Self::reg => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_abcd => match ty.size().bits() { - 16 => Some(('x', "ax")), - 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax")), + 16 => Some(('x', "ax", 16).into()), + 32 if arch == InlineAsmArch::X86_64 => Some(('e', "eax", 32).into()), _ => None, }, Self::reg_byte => None, Self::xmm_reg => None, Self::ymm_reg => match ty.size().bits() { 256 => None, - _ => Some(('x', "xmm0")), + _ => Some(('x', "xmm0", 128).into()), }, Self::zmm_reg => match ty.size().bits() { 512 => None, - 256 => Some(('y', "ymm0")), - _ => Some(('x', "xmm0")), + 256 => Some(('y', "ymm0", 256).into()), + _ => Some(('x', "xmm0", 128).into()), }, Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, @@ -86,19 +82,19 @@ impl X86InlineAsmRegClass { } } - pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str)> { + pub fn default_modifier(self, arch: InlineAsmArch) -> Option<ModifierInfo> { match self { Self::reg | Self::reg_abcd => { if arch == InlineAsmArch::X86_64 { - Some(('r', "rax")) + Some(('r', "rax", 64).into()) } else { - Some(('e', "eax")) + Some(('e', "eax", 32).into()) } } Self::reg_byte => None, - Self::xmm_reg => Some(('x', "xmm0")), - Self::ymm_reg => Some(('y', "ymm0")), - Self::zmm_reg => Some(('z', "zmm0")), + Self::xmm_reg => Some(('x', "xmm0", 128).into()), + Self::ymm_reg => Some(('y', "ymm0", 256).into()), + Self::zmm_reg => Some(('z', "zmm0", 512).into()), Self::kreg | Self::kreg0 => None, Self::mmx_reg | Self::x87_reg => None, Self::tmm_reg => None, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index bd737e6ab82..28f4f81e7d8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -768,7 +768,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } // Different to previous arm because one is `&hir::Local` and the other // is `P<hir::Local>`. - hir::Node::Local(local) => get_name(err, &local.pat.kind), + hir::Node::LetStmt(local) => get_name(err, &local.pat.kind), _ => None, } } @@ -930,7 +930,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { return; }; - let hir::Node::Local(hir::Local { ty: None, init: Some(init), .. }) = + let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = self.tcx.parent_hir_node(pat.hir_id) else { return; @@ -1562,7 +1562,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let Res::Local(hir_id) = path.res && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let None = local.ty && let Some(binding_expr) = local.init { @@ -2966,7 +2966,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { err.downgrade_to_delayed_bug(); } match tcx.parent_hir_node(hir_id) { - Node::Local(hir::Local { ty: Some(ty), .. }) => { + Node::LetStmt(hir::LetStmt { ty: Some(ty), .. }) => { err.span_suggestion_verbose( ty.span.shrink_to_lo(), "consider borrowing here", @@ -2975,7 +2975,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { ); err.note("all local variables must have a statically known size"); } - Node::Local(hir::Local { + Node::LetStmt(hir::LetStmt { init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), .. }) => { @@ -3867,7 +3867,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind && let hir::Path { res: Res::Local(hir_id), .. } = path && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) - && let hir::Node::Local(local) = self.tcx.parent_hir_node(binding.hir_id) + && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) && let Some(binding_expr) = local.init { // If the expression we're calling on is a binding, we want to point at the @@ -4128,7 +4128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 01f9c8bb5d1..9444cf8248e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -1272,7 +1272,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { { let parent = self.tcx.parent_hir_node(binding.hir_id); // We've reached the root of the method call chain... - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && let Some(binding_expr) = local.init { // ...and it is a binding. Get the binding creation and continue the chain. diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 48e76d50be1..9c3d39307b2 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -10,6 +10,7 @@ use rustc_middle::ty::layout::{ use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo}; +use rustc_span::sym; use rustc_span::symbol::Symbol; use rustc_target::abi::*; @@ -1007,6 +1008,7 @@ fn variant_info_for_adt<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1090,6 +1092,7 @@ fn variant_info_for_coroutine<'tcx>( offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + type_name: None, } }) .collect(); @@ -1104,19 +1107,24 @@ fn variant_info_for_coroutine<'tcx>( .iter() .enumerate() .map(|(field_idx, local)| { + let field_name = coroutine.field_names[*local]; let field_layout = variant_layout.field(cx, field_idx); let offset = variant_layout.fields.offset(field_idx); // The struct is as large as the last field's end variant_size = variant_size.max(offset + field_layout.size); FieldInfo { kind: FieldKind::CoroutineLocal, - name: coroutine.field_names[*local].unwrap_or(Symbol::intern(&format!( + name: field_name.unwrap_or(Symbol::intern(&format!( ".coroutine_field{}", local.as_usize() ))), offset: offset.bytes(), size: field_layout.size.bytes(), align: field_layout.align.abi.bytes(), + // Include the type name if there is no field name, or if the name is the + // __awaitee placeholder symbol which means a child future being `.await`ed. + type_name: (field_name.is_none() || field_name == Some(sym::__awaitee)) + .then(|| Symbol::intern(&field_layout.ty.to_string())), } }) .chain(upvar_fields.iter().copied()) diff --git a/compiler/stable_mir/src/mir/alloc.rs b/compiler/stable_mir/src/mir/alloc.rs index 66457933438..408e0bafa58 100644 --- a/compiler/stable_mir/src/mir/alloc.rs +++ b/compiler/stable_mir/src/mir/alloc.rs @@ -55,7 +55,7 @@ impl IndexedVal for AllocId { /// Utility function used to read an allocation data into a unassigned integer. pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> { let mut buf = [0u8; std::mem::size_of::<u128>()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; Ok(u128::from_le_bytes(buf)) @@ -70,7 +70,7 @@ pub(crate) fn read_target_uint(mut bytes: &[u8]) -> Result<u128, Error> { /// Utility function used to read an allocation data into an assigned integer. pub(crate) fn read_target_int(mut bytes: &[u8]) -> Result<i128, Error> { let mut buf = [0u8; std::mem::size_of::<i128>()]; - match MachineInfo::target_endianess() { + match MachineInfo::target_endianness() { Endian::Little => { bytes.read_exact(&mut buf[..bytes.len()])?; Ok(i128::from_le_bytes(buf)) diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index e4a012d8c47..7c536a3e914 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -621,7 +621,7 @@ impl Rvalue { Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(..), _) => { Ok(Ty::usize_ty()) } - Rvalue::NullaryOp(NullOp::UbCheck(_), _) => Ok(Ty::bool_ty()), + Rvalue::NullaryOp(NullOp::UbChecks, _) => Ok(Ty::bool_ty()), Rvalue::Aggregate(ak, ops) => match *ak { AggregateKind::Array(ty) => Ty::try_new_array(ty, ops.len() as u64), AggregateKind::Tuple => Ok(Ty::new_tuple( @@ -989,13 +989,7 @@ pub enum NullOp { /// Returns the offset of a field. OffsetOf(Vec<(VariantIdx, FieldIdx)>), /// cfg!(debug_assertions), but at codegen time - UbCheck(UbKind), -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum UbKind { - LanguageUb, - LibraryUb, + UbChecks, } impl Operand { diff --git a/compiler/stable_mir/src/target.rs b/compiler/stable_mir/src/target.rs index 3a9011a2ffe..e00a418c540 100644 --- a/compiler/stable_mir/src/target.rs +++ b/compiler/stable_mir/src/target.rs @@ -14,7 +14,7 @@ impl MachineInfo { with(|cx| cx.target_info()) } - pub fn target_endianess() -> Endian { + pub fn target_endianness() -> Endian { with(|cx| cx.target_info().endian) } diff --git a/library/core/src/char/convert.rs b/library/core/src/char/convert.rs index 70b9e89f9ea..8f612929110 100644 --- a/library/core/src/char/convert.rs +++ b/library/core/src/char/convert.rs @@ -4,9 +4,9 @@ use crate::char::TryFromCharError; use crate::convert::TryFrom; use crate::error::Error; use crate::fmt; -use crate::intrinsics::assert_unsafe_precondition; use crate::mem::transmute; use crate::str::FromStr; +use crate::ub_checks::assert_unsafe_precondition; /// Converts a `u32` to a `char`. See [`char::from_u32`]. #[must_use] diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index ffe059bf65c..b27d0db4619 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -4,6 +4,7 @@ //! Hints may be compile time or runtime. use crate::intrinsics; +use crate::ub_checks; /// Informs the compiler that the site which is calling this function is not /// reachable, possibly enabling further optimizations. @@ -98,7 +99,7 @@ use crate::intrinsics; #[rustc_const_stable(feature = "const_unreachable_unchecked", since = "1.57.0")] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unreachable_unchecked() -> ! { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::unreachable_unchecked must never be reached", () => false @@ -148,7 +149,7 @@ pub const unsafe fn unreachable_unchecked() -> ! { pub const unsafe fn assert_unchecked(cond: bool) { // SAFETY: The caller promised `cond` is true. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "hint::assert_unchecked must never be called when the condition is false", (cond: bool = cond) => cond, diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 613d0ab212a..dec31548fc8 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -66,6 +66,8 @@ use crate::marker::DiscriminantKind; use crate::marker::Tuple; use crate::mem::align_of; +use crate::ptr; +use crate::ub_checks; pub mod mir; pub mod simd; @@ -1163,14 +1165,6 @@ extern "rust-intrinsic" { /// may lead to unexpected and unstable compilation results. This makes `transmute` **incredibly /// unsafe**. `transmute` should be the absolute last resort. /// - /// Transmuting pointers *to* integers in a `const` context is [undefined behavior][ub], - /// unless the pointer was originally created *from* an integer. - /// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], - /// but also semantically-equivalent conversions such as punning through `repr(C)` union fields.) - /// Any attempt to use the resulting value for integer operations will abort const-evaluation. - /// (And even outside `const`, such transmutation is touching on many unspecified aspects of the - /// Rust memory model and should be avoided. See below for alternatives.) - /// /// Because `transmute` is a by-value operation, alignment of the *transmuted values /// themselves* is not a concern. As with any other function, the compiler already ensures /// both `Src` and `Dst` are properly aligned. However, when transmuting values that *point @@ -1181,6 +1175,39 @@ extern "rust-intrinsic" { /// /// [ub]: ../../reference/behavior-considered-undefined.html /// + /// # Transmutation between pointers and integers + /// + /// Special care has to be taken when transmuting between pointers and integers, e.g. + /// transmuting between `*const ()` and `usize`. + /// + /// Transmuting *pointers to integers* in a `const` context is [undefined behavior][ub], unless + /// the pointer was originally created *from* an integer. (That includes this function + /// specifically, integer-to-pointer casts, and helpers like [`dangling`][crate::ptr::dangling], + /// but also semantically-equivalent conversions such as punning through `repr(C)` union + /// fields.) Any attempt to use the resulting value for integer operations will abort + /// const-evaluation. (And even outside `const`, such transmutation is touching on many + /// unspecified aspects of the Rust memory model and should be avoided. See below for + /// alternatives.) + /// + /// Transmuting *integers to pointers* is a largely unspecified operation. It is likely *not* + /// equivalent to an `as` cast. Doing non-zero-sized memory accesses with a pointer constructed + /// this way is currently considered undefined behavior. + /// + /// All this also applies when the integer is nested inside an array, tuple, struct, or enum. + /// However, `MaybeUninit<usize>` is not considered an integer type for the purpose of this + /// section. Transmuting `*const ()` to `MaybeUninit<usize>` is fine---but then calling + /// `assume_init()` on that result is considered as completing the pointer-to-integer transmute + /// and thus runs into the issues discussed above. + /// + /// In particular, doing a pointer-to-integer-to-pointer roundtrip via `transmute` is *not* a + /// lossless process. If you want to round-trip a pointer through an integer in a way that you + /// can get back the original pointer, you need to use `as` casts, or replace the integer type + /// by `MaybeUninit<$int>` (and never call `assume_init()`). If you are looking for a way to + /// store data of arbitrary type, also use `MaybeUninit<T>` (that will also handle uninitialized + /// memory due to padding). If you specifically need to store something that is "either an + /// integer or a pointer", use `*mut ()`: integers can be converted to pointers and back without + /// any loss (via `as` casts or via `transmute`). + /// /// # Examples /// /// There are a few things that `transmute` is really useful for. @@ -2638,38 +2665,43 @@ pub const fn is_val_statically_known<T: Copy>(_arg: T) -> bool { false } -/// Returns whether we should check for library UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// Non-overlapping *typed* swap of a single value. /// -/// This intrinsic is evaluated after monomorphization, and therefore branching on this value can -/// be used to implement debug assertions that are included in the precompiled standard library, -/// but can be optimized out by builds that monomorphize the standard library code with debug -/// assertions disabled. This intrinsic is primarily used by [`assert_unsafe_precondition`]. +/// The codegen backends will replace this with a better implementation when +/// `T` is a simple type that can be loaded and stored as an immediate. /// -/// We have separate intrinsics for library UB and language UB because checkers like the const-eval -/// interpreter and Miri already implement checks for language UB. Since such checkers do not know -/// about library preconditions, checks guarded by this intrinsic let them find more UB. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] -#[unstable(feature = "core_intrinsics", issue = "none")] -#[inline(always)] -#[rustc_intrinsic] -pub(crate) const fn check_library_ub() -> bool { - cfg!(debug_assertions) -} - -/// Returns whether we should check for language UB. This evaluate to the value of `cfg!(debug_assertions)` -/// during monomorphization. +/// The stabilized form of this intrinsic is [`crate::mem::swap`]. /// -/// Since checks implemented at the source level must come strictly before the operation that -/// executes UB, if we enabled language UB checks in const-eval/Miri we would miss out on the -/// interpreter's improved diagnostics for the cases that our source-level checks catch. +/// # Safety /// -/// See `check_library_ub` for more information. -#[rustc_const_unstable(feature = "ub_checks", issue = "none")] +/// `x` and `y` are readable and writable as `T`, and non-overlapping. +#[rustc_nounwind] +#[inline] +#[cfg_attr(not(bootstrap), rustc_intrinsic)] +// This has fallback `const fn` MIR, so shouldn't need stability, see #122652 +#[rustc_const_unstable(feature = "const_typed_swap", issue = "none")] +pub const unsafe fn typed_swap<T>(x: *mut T, y: *mut T) { + // SAFETY: The caller provided single non-overlapping items behind + // pointers, so swapping them with `count: 1` is fine. + unsafe { ptr::swap_nonoverlapping(x, y, 1) }; +} + +/// Returns whether we should perform some UB-checking at runtime. This evaluate to the value of +/// `cfg!(debug_assertions)` during monomorphization. +/// +/// This intrinsic is evaluated after monomorphization, which is relevant when mixing crates +/// compiled with and without debug_assertions. The common case here is a user program built with +/// debug_assertions linked against the distributed sysroot which is built without debug_assertions. +/// For code that gets monomorphized in the user crate (i.e., generic functions and functions with +/// `#[inline]`), gating assertions on `ub_checks()` rather than `cfg!(debug_assertions)` means that +/// assertions are enabled whenever the *user crate* has debug assertions enabled. However if the +/// user has debug assertions disabled, the checks will still get optimized out. This intrinsic is +/// primarily used by [`ub_checks::assert_unsafe_precondition`]. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] #[unstable(feature = "core_intrinsics", issue = "none")] #[inline(always)] -#[rustc_intrinsic] -pub(crate) const fn check_language_ub() -> bool { +#[cfg_attr(not(bootstrap), rustc_intrinsic)] // just make it a regular fn in bootstrap +pub(crate) const fn ub_checks() -> bool { cfg!(debug_assertions) } @@ -2733,132 +2765,6 @@ pub unsafe fn vtable_align(_ptr: *const ()) -> usize { // (`transmute` also falls into this category, but it cannot be wrapped due to the // check that `T` and `U` have the same size.) -/// Check that the preconditions of an unsafe function are followed. The check is enabled at -/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri -/// checks implemented with this macro for language UB are always ignored. -/// -/// This macro should be called as -/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` -/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all -/// those arguments are passed to a function with the body `check_expr`. -/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB -/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation -/// of a documented library precondition that does not *immediately* lead to language UB. -/// -/// If `check_library_ub` is used but the check is actually guarding language UB, the check will -/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice -/// diagnostic, but our ability to detect UB is unchanged. -/// But if `check_language_ub` is used when the check is actually for library UB, the check is -/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the -/// library UB, the backtrace Miri reports may be far removed from original cause. -/// -/// These checks are behind a condition which is evaluated at codegen time, not expansion time like -/// [`debug_assert`]. This means that a standard library built with optimizations and debug -/// assertions disabled will have these checks optimized out of its monomorphizations, but if a -/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of -/// this macro, that monomorphization will contain the check. -/// -/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and -/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to -/// this structure: -/// ```ignore (pseudocode) -/// if ::core::intrinsics::check_language_ub() { -/// precondition_check(args) -/// } -/// ``` -/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and -/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is -/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in -/// MIR, but *can* be inlined and fully optimized by a codegen backend. -/// -/// Callers should avoid introducing any other `let` bindings or any code outside this macro in -/// order to call it. Since the precompiled standard library is built with full debuginfo and these -/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough -/// debuginfo to have a measurable compile-time impact on debug builds. -#[allow_internal_unstable(ub_checks)] // permit this to be called in stably-const fn -macro_rules! assert_unsafe_precondition { - ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { - { - // This check is inlineable, but not by the MIR inliner. - // The reason for this is that the MIR inliner is in an exceptionally bad position - // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, - // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and - // would be bad for compile times. - // - // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without - // inlining the check. If it's `true`, it can inline it and get significantly better performance. - #[rustc_no_mir_inline] - #[inline] - #[rustc_nounwind] - #[rustc_const_unstable(feature = "ub_checks", issue = "none")] - const fn precondition_check($($name:$ty),*) { - if !$e { - ::core::panicking::panic_nounwind( - concat!("unsafe precondition(s) violated: ", $message) - ); - } - } - - if ::core::intrinsics::$kind() { - precondition_check($($arg,)*); - } - } - }; -} -pub(crate) use assert_unsafe_precondition; - -/// Checks whether `ptr` is properly aligned with respect to -/// `align_of::<T>()`. -/// -/// In `const` this is approximate and can fail spuriously. It is primarily intended -/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the -/// check is anyway not executed in `const`. -#[inline] -pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { - !ptr.is_null() && ptr.is_aligned_to(align) -} - -#[inline] -pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { - let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; - len <= max_len -} - -/// Checks whether the regions of memory starting at `src` and `dst` of size -/// `count * size` do *not* overlap. -/// -/// Note that in const-eval this function just returns `true` and therefore must -/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. -#[inline] -pub(crate) const fn is_nonoverlapping( - src: *const (), - dst: *const (), - size: usize, - count: usize, -) -> bool { - #[inline] - fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { - let src_usize = src.addr(); - let dst_usize = dst.addr(); - let Some(size) = size.checked_mul(count) else { - crate::panicking::panic_nounwind( - "is_nonoverlapping: `size_of::<T>() * count` overflows a usize", - ) - }; - let diff = src_usize.abs_diff(dst_usize); - // If the absolute distance between the ptrs is at least as big as the size of the buffer, - // they do not overlap. - diff >= size - } - - #[inline] - const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { - true - } - - const_eval_select((src, dst, size, count), comptime, runtime) -} - /// Copies `count * size_of::<T>()` bytes from `src` to `dst`. The source /// and destination must *not* overlap. /// @@ -2957,7 +2863,7 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us pub fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize); } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -2968,9 +2874,9 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us align: usize = align_of::<T>(), count: usize = count, ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) - && is_nonoverlapping(src, dst, size, count) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) + && ub_checks::is_nonoverlapping(src, dst, size, count) ); // SAFETY: the safety contract for `copy_nonoverlapping` must be @@ -3061,7 +2967,7 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { // SAFETY: the safety contract for `copy` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::copy_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -3070,8 +2976,8 @@ pub const unsafe fn copy<T>(src: *const T, dst: *mut T, count: usize) { dst: *mut () = dst as *mut (), align: usize = align_of::<T>(), ) => - is_aligned_and_not_null(src, align) - && is_aligned_and_not_null(dst, align) + ub_checks::is_aligned_and_not_null(src, align) + && ub_checks::is_aligned_and_not_null(dst, align) ); copy(src, dst, count) } @@ -3142,13 +3048,13 @@ pub const unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize) { // SAFETY: the safety contract for `write_bytes` must be upheld by the caller. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_bytes requires that the destination pointer is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); write_bytes(dst, val, count) } diff --git a/library/core/src/intrinsics/simd.rs b/library/core/src/intrinsics/simd.rs index b69f4f853b9..427a95f4665 100644 --- a/library/core/src/intrinsics/simd.rs +++ b/library/core/src/intrinsics/simd.rs @@ -470,7 +470,7 @@ extern "rust-intrinsic" { /// No matter whether the output is an array or an unsigned integer, it is treated as a single /// contiguous list of bits. The bitmask is always packed on the least-significant side of the /// output, and padded with 0s in the most-significant bits. The order of the bits depends on - /// endianess: + /// endianness: /// /// * On little endian, the least significant bit corresponds to the first vector element. /// * On big endian, the least significant bit corresponds to the last vector element. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 2718dd11473..f0448a98981 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -170,6 +170,8 @@ #![feature(const_try)] #![feature(const_type_id)] #![feature(const_type_name)] +#![feature(const_typed_swap)] +#![feature(const_ub_checks)] #![feature(const_unicode_case_lookup)] #![feature(const_unsafecell_get_mut)] #![feature(const_waker)] @@ -189,7 +191,6 @@ #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_ptr_get)] -#![feature(slice_split_at_unchecked)] #![feature(split_at_checked)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -366,6 +367,7 @@ pub mod hint; pub mod intrinsics; pub mod mem; pub mod ptr; +mod ub_checks; /* Core language traits */ diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs index d1dc6720271..75d42edbaa0 100644 --- a/library/core/src/mem/mod.rs +++ b/library/core/src/mem/mod.rs @@ -726,63 +726,9 @@ pub unsafe fn uninitialized<T>() -> T { #[rustc_const_unstable(feature = "const_swap", issue = "83163")] #[rustc_diagnostic_item = "mem_swap"] pub const fn swap<T>(x: &mut T, y: &mut T) { - // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary - // reinterpretation of values as (chunkable) byte arrays, and the loop in the - // block optimization in `swap_slice` is hard to rewrite back - // into the (unoptimized) direct swapping implementation, so we disable it. - #[cfg(not(any(target_arch = "spirv")))] - { - // For types that are larger multiples of their alignment, the simple way - // tends to copy the whole thing to stack rather than doing it one part - // at a time, so instead treat them as one-element slices and piggy-back - // the slice optimizations that will split up the swaps. - if const { size_of::<T>() / align_of::<T>() > 2 } { - // SAFETY: exclusive references always point to one non-overlapping - // element and are non-null and properly aligned. - return unsafe { ptr::swap_nonoverlapping(x, y, 1) }; - } - } - - // If a scalar consists of just a small number of alignment units, let - // the codegen just swap those pieces directly, as it's likely just a - // few instructions and anything else is probably overcomplicated. - // - // Most importantly, this covers primitives and simd types that tend to - // have size=align where doing anything else can be a pessimization. - // (This will also be used for ZSTs, though any solution works for them.) - swap_simple(x, y); -} - -/// Same as [`swap`] semantically, but always uses the simple implementation. -/// -/// Used elsewhere in `mem` and `ptr` at the bottom layer of calls. -#[rustc_const_unstable(feature = "const_swap", issue = "83163")] -#[inline] -pub(crate) const fn swap_simple<T>(x: &mut T, y: &mut T) { - // We arrange for this to typically be called with small types, - // so this reads-and-writes approach is actually better than using - // copy_nonoverlapping as it easily puts things in LLVM registers - // directly and doesn't end up inlining allocas. - // And LLVM actually optimizes it to 3×memcpy if called with - // a type larger than it's willing to keep in a register. - // Having typed reads and writes in MIR here is also good as - // it lets Miri and CTFE understand them better, including things - // like enforcing type validity for them. - // Importantly, read+copy_nonoverlapping+write introduces confusing - // asymmetry to the behaviour where one value went through read+write - // whereas the other was copied over by the intrinsic (see #94371). - // Furthermore, using only read+write here benefits limited backends - // such as SPIR-V that work on an underlying *typed* view of memory, - // and thus have trouble with Rust's untyped memory operations. - - // SAFETY: exclusive references are always valid to read/write, - // including being aligned, and nothing here panics so it's drop-safe. - unsafe { - let a = ptr::read(x); - let b = ptr::read(y); - ptr::write(x, b); - ptr::write(y, a); - } + // SAFETY: `&mut` guarantees these are typed readable and writable + // as well as non-overlapping. + unsafe { intrinsics::typed_swap(x, y) } } /// Replaces `dest` with the default value of `T`, returning the previous `dest` value. diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs index a8f637280df..c65ffbb98f2 100644 --- a/library/core/src/num/nonzero.rs +++ b/library/core/src/num/nonzero.rs @@ -9,6 +9,7 @@ use crate::ops::{BitOr, BitOrAssign, Div, DivAssign, Neg, Rem, RemAssign}; use crate::panic::{RefUnwindSafe, UnwindSafe}; use crate::ptr; use crate::str::FromStr; +use crate::ub_checks; use super::from_str_radix; use super::{IntErrorKind, ParseIntError}; @@ -369,7 +370,7 @@ where None => { // SAFETY: The caller guarantees that `n` is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "NonZero::new_unchecked requires the argument to be non-zero", () => false, @@ -409,7 +410,7 @@ where None => { // SAFETY: The caller guarantees that `n` references a value that is non-zero, so this is unreachable. unsafe { - intrinsics::assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "NonZero::from_mut_unchecked requires the argument to dereference as non-zero", () => false, diff --git a/library/core/src/ops/index_range.rs b/library/core/src/ops/index_range.rs index b28d88fa5bf..65bda9177c7 100644 --- a/library/core/src/ops/index_range.rs +++ b/library/core/src/ops/index_range.rs @@ -1,6 +1,7 @@ -use crate::intrinsics::{assert_unsafe_precondition, unchecked_add, unchecked_sub}; +use crate::intrinsics::{unchecked_add, unchecked_sub}; use crate::iter::{FusedIterator, TrustedLen}; use crate::num::NonZero; +use crate::ub_checks; /// Like a `Range<usize>`, but with a safety invariant that `start <= end`. /// @@ -19,7 +20,7 @@ impl IndexRange { /// - `start <= end` #[inline] pub const unsafe fn new_unchecked(start: usize, end: usize) -> Self { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_library_ub, "IndexRange::new_unchecked requires `start <= end`", (start: usize = start, end: usize = end) => start <= end, diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs index c77e9675a6a..40326221258 100644 --- a/library/core/src/panic/panic_info.rs +++ b/library/core/src/panic/panic_info.rs @@ -161,11 +161,12 @@ impl fmt::Display for PanicInfo<'_> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { formatter.write_str("panicked at ")?; self.location.fmt(formatter)?; + formatter.write_str(":")?; if let Some(message) = self.message { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_fmt(*message)?; } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() { - formatter.write_str(":\n")?; + formatter.write_str("\n")?; formatter.write_str(payload)?; } // NOTE: we cannot use downcast_ref::<String>() here diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9e8dac88816..a8940d9cd1e 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -132,11 +132,11 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[rustc_const_unstable(feature = "panic_internals", issue = "none")] #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators pub const fn panic(expr: &'static str) -> ! { - // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially + // Use Arguments::new_const instead of format_args!("{expr}") to potentially // reduce size overhead. The format_args! macro uses str's Display trait to // write expr, which calls Formatter::pad, which must accommodate string // truncation and padding (even though none is used here). Using - // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the + // Arguments::new_const may allow the compiler to omit Formatter::pad from the // output binary, saving up to a few kilobytes. panic_fmt(fmt::Arguments::new_const(&[expr])); } diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index a0227d9130b..d14cac9afb5 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -144,7 +144,7 @@ //! * e.g. [`drop`]ping the [`Future`] [^pin-drop-future] //! //! There are two possible ways to ensure the invariants required for 2. and 3. above (which -//! apply to any address-sensitive type, not just self-referrential types) do not get broken. +//! apply to any address-sensitive type, not just self-referential types) do not get broken. //! //! 1. Have the value detect when it is moved and update all the pointers that point to itself. //! 2. Guarantee that the address of the value does not change (and that memory is not re-used @@ -170,7 +170,7 @@ //! become viral throughout all code that interacts with the object. //! //! The second option is a viable solution to the problem for some use cases, in particular -//! for self-referrential types. Under this model, any type that has an address sensitive state +//! for self-referential types. Under this model, any type that has an address sensitive state //! would ultimately store its data in something like a [`Box<T>`], carefully manage internal //! access to that data to ensure no *moves* or other invalidation occurs, and finally //! provide a safe interface on top. @@ -186,8 +186,8 @@ //! //! Although there were other reason as well, this issue of expensive composition is the key thing //! that drove Rust towards adopting a different model. It is particularly a problem -//! when one considers, for exapmle, the implications of composing together the [`Future`]s which -//! will eventaully make up an asynchronous task (including address-sensitive `async fn` state +//! when one considers, for example, the implications of composing together the [`Future`]s which +//! will eventually make up an asynchronous task (including address-sensitive `async fn` state //! machines). It is plausible that there could be many layers of [`Future`]s composed together, //! including multiple layers of `async fn`s handling different parts of a task. It was deemed //! unacceptable to force indirection and allocation for each layer of composition in this case. @@ -359,7 +359,7 @@ //! Builtin types that are [`Unpin`] include all of the primitive types, like [`bool`], [`i32`], //! and [`f32`], references (<code>[&]T</code> and <code>[&mut] T</code>), etc., as well as many //! core and standard library types like [`Box<T>`], [`String`], and more. -//! These types are marked [`Unpin`] because they do not have an ddress-sensitive state like the +//! These types are marked [`Unpin`] because they do not have an address-sensitive state like the //! ones we discussed above. If they did have such a state, those parts of their interface would be //! unsound without being expressed through pinning, and they would then need to not //! implement [`Unpin`]. @@ -953,7 +953,7 @@ use crate::{ /// discussed below. /// /// We call such a [`Pin`]-wrapped pointer a **pinning pointer** (or pinning ref, or pinning -/// [`Box`], etc.) because its existince is the thing that is pinning the underlying pointee in +/// [`Box`], etc.) because its existence is the thing that is pinning the underlying pointee in /// place: it is the metaphorical "pin" securing the data in place on the pinboard (in memory). /// /// It is important to stress that the thing in the [`Pin`] is not the value which we want to pin @@ -962,7 +962,7 @@ use crate::{ /// /// The most common set of types which require pinning related guarantees for soundness are the /// compiler-generated state machines that implement [`Future`] for the return value of -/// `async fn`s. These compiler-generated [`Future`]s may contain self-referrential pointers, one +/// `async fn`s. These compiler-generated [`Future`]s may contain self-referential pointers, one /// of the most common use cases for [`Pin`]. More details on this point are provided in the /// [`pin` module] docs, but suffice it to say they require the guarantees provided by pinning to /// be implemented soundly. diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs index 8f44b7eb7c2..bc84fb5ccb0 100644 --- a/library/core/src/ptr/alignment.rs +++ b/library/core/src/ptr/alignment.rs @@ -1,7 +1,7 @@ use crate::convert::{TryFrom, TryInto}; -#[cfg(debug_assertions)] -use crate::intrinsics::assert_unsafe_precondition; use crate::num::NonZero; +#[cfg(debug_assertions)] +use crate::ub_checks::assert_unsafe_precondition; use crate::{cmp, fmt, hash, mem, num}; /// A type storing a `usize` which is a power of two, and thus diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 69c61602073..a6c00ff28d4 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -818,7 +818,7 @@ impl<T: ?Sized> *const T { intrinsics::const_eval_select((this, origin), comptime, runtime) } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::sub_ptr requires `self >= origin`", ( diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index 1f0204daf72..56378b437e7 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -388,10 +388,9 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; -use crate::intrinsics::{ - self, assert_unsafe_precondition, is_aligned_and_not_null, is_nonoverlapping, -}; +use crate::intrinsics; use crate::marker::FnPtr; +use crate::ub_checks; use crate::mem::{self, align_of, size_of, MaybeUninit}; @@ -1019,7 +1018,7 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { }; } - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::swap_nonoverlapping requires that both pointer arguments are aligned and non-null \ and the specified memory ranges do not overlap", @@ -1030,9 +1029,9 @@ pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) { align: usize = align_of::<T>(), count: usize = count, ) => - is_aligned_and_not_null(x, align) - && is_aligned_and_not_null(y, align) - && is_nonoverlapping(x, y, size, count) + ub_checks::is_aligned_and_not_null(x, align) + && ub_checks::is_aligned_and_not_null(y, align) + && ub_checks::is_nonoverlapping(x, y, size, count) ); // Split up the slice into small power-of-two-sized chunks that LLVM is able @@ -1062,11 +1061,26 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun let mut i = 0; while i < count { // SAFETY: By precondition, `i` is in-bounds because it's below `n` - let x = unsafe { &mut *x.add(i) }; + let x = unsafe { x.add(i) }; // SAFETY: By precondition, `i` is in-bounds because it's below `n` // and it's distinct from `x` since the ranges are non-overlapping - let y = unsafe { &mut *y.add(i) }; - mem::swap_simple::<MaybeUninit<T>>(x, y); + let y = unsafe { y.add(i) }; + + // If we end up here, it's because we're using a simple type -- like + // a small power-of-two-sized thing -- or a special type with particularly + // large alignment, particularly SIMD types. + // Thus we're fine just reading-and-writing it, as either it's small + // and that works well anyway or it's special and the type's author + // presumably wanted things to be done in the larger chunk. + + // SAFETY: we're only ever given pointers that are valid to read/write, + // including being aligned, and nothing here panics so it's drop-safe. + unsafe { + let a: MaybeUninit<T> = read(x); + let b: MaybeUninit<T> = read(y); + write(x, b); + write(y, a); + } i += 1; } @@ -1120,13 +1134,13 @@ pub const unsafe fn replace<T>(dst: *mut T, src: T) -> T { // and cannot overlap `src` since `dst` must point to a distinct // allocated object. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::replace requires that the pointer argument is aligned and non-null", ( addr: *const () = dst as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); mem::replace(&mut *dst, src) } @@ -1272,13 +1286,13 @@ pub const unsafe fn read<T>(src: *const T) -> T { // SAFETY: the caller must guarantee that `src` is valid for reads. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); crate::intrinsics::read_via_copy(src) } @@ -1481,13 +1495,13 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) { // to `dst` while `src` is owned by this function. unsafe { #[cfg(debug_assertions)] // Too expensive to always enable (for now?) - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::write_via_move(dst, src) } @@ -1653,13 +1667,13 @@ pub const unsafe fn write_unaligned<T>(dst: *mut T, src: T) { pub unsafe fn read_volatile<T>(src: *const T) -> T { // SAFETY: the caller must uphold the safety contract for `volatile_load`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::read_volatile requires that the pointer argument is aligned and non-null", ( addr: *const () = src as *const (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_load(src) } @@ -1732,13 +1746,13 @@ pub unsafe fn read_volatile<T>(src: *const T) -> T { pub unsafe fn write_volatile<T>(dst: *mut T, src: T) { // SAFETY: the caller must uphold the safety contract for `volatile_store`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "ptr::write_volatile requires that the pointer argument is aligned and non-null", ( addr: *mut () = dst as *mut (), align: usize = align_of::<T>(), - ) => is_aligned_and_not_null(addr, align) + ) => ub_checks::is_aligned_and_not_null(addr, align) ); intrinsics::volatile_store(dst, src); } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 2ac42e20d43..e9488917acc 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -2,7 +2,6 @@ use crate::cmp::Ordering; use crate::fmt; use crate::hash; use crate::intrinsics; -use crate::intrinsics::assert_unsafe_precondition; use crate::marker::Unsize; use crate::mem::{MaybeUninit, SizedTypeProperties}; use crate::num::NonZero; @@ -10,6 +9,7 @@ use crate::ops::{CoerceUnsized, DispatchFromDyn}; use crate::ptr; use crate::ptr::Unique; use crate::slice::{self, SliceIndex}; +use crate::ub_checks::assert_unsafe_precondition; /// `*mut T` but non-zero and [covariant]. /// diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 210118817ab..127a407dae5 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,10 +1,10 @@ //! Indexing implementations for `[T]`. -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::const_eval_select; use crate::intrinsics::unchecked_sub; use crate::ops; use crate::ptr; +use crate::ub_checks::assert_unsafe_precondition; #[stable(feature = "rust1", since = "1.0.0")] impl<T, I> ops::Index<I> for [T] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 4a574bf0347..a16005abf46 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -9,7 +9,6 @@ use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::fmt; use crate::hint; -use crate::intrinsics::assert_unsafe_precondition; use crate::intrinsics::exact_div; use crate::mem::{self, SizedTypeProperties}; use crate::num::NonZero; @@ -17,6 +16,7 @@ use crate::ops::{Bound, OneSidedRange, Range, RangeBounds}; use crate::ptr; use crate::simd::{self, Simd}; use crate::slice; +use crate::ub_checks::assert_unsafe_precondition; #[unstable( feature = "slice_internals", @@ -1944,8 +1944,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let v = [1, 2, 3, 4, 5, 6]; /// /// unsafe { @@ -1966,7 +1964,7 @@ impl<T> [T] { /// assert_eq!(right, []); /// } /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "const_slice_split_at_unchecked", since = "1.77.0")] #[inline] #[must_use] @@ -2008,8 +2006,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_split_at_unchecked)] - /// /// let mut v = [1, 0, 3, 0, 5, 6]; /// // scoped to restrict the lifetime of the borrows /// unsafe { @@ -2021,7 +2017,7 @@ impl<T> [T] { /// } /// assert_eq!(v, [1, 2, 3, 4, 5, 6]); /// ``` - #[unstable(feature = "slice_split_at_unchecked", reason = "new API", issue = "76014")] + #[stable(feature = "slice_split_at_unchecked", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_slice_split_at_mut", issue = "101804")] #[inline] #[must_use] diff --git a/library/core/src/slice/raw.rs b/library/core/src/slice/raw.rs index 2199614ce27..29a12f106c5 100644 --- a/library/core/src/slice/raw.rs +++ b/library/core/src/slice/raw.rs @@ -1,12 +1,10 @@ //! Free functions to create `&[T]` and `&mut [T]`. use crate::array; -use crate::intrinsics::{ - assert_unsafe_precondition, is_aligned_and_not_null, is_valid_allocation_size, -}; use crate::mem::{align_of, size_of}; use crate::ops::Range; use crate::ptr; +use crate::ub_checks; /// Forms a slice from a pointer and a length. /// @@ -95,7 +93,7 @@ use crate::ptr; pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -104,8 +102,8 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] align: usize = align_of::<T>(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &*ptr::slice_from_raw_parts(data, len) } @@ -149,7 +147,7 @@ pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] { // SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`. unsafe { - assert_unsafe_precondition!( + ub_checks::assert_unsafe_precondition!( check_language_ub, "slice::from_raw_parts_mut requires the pointer to be aligned and non-null, and the total size of the slice not to exceed `isize::MAX`", ( @@ -158,8 +156,8 @@ pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a m align: usize = align_of::<T>(), len: usize = len, ) => - is_aligned_and_not_null(data, align) - && is_valid_allocation_size(size, len) + ub_checks::is_aligned_and_not_null(data, align) + && ub_checks::is_valid_allocation_size(size, len) ); &mut *ptr::slice_from_raw_parts_mut(data, len) } diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index ec81fd095d5..672af752149 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -1,10 +1,10 @@ //! Trait implementations for `str`. use crate::cmp::Ordering; -use crate::intrinsics::assert_unsafe_precondition; use crate::ops; use crate::ptr; use crate::slice::SliceIndex; +use crate::ub_checks::assert_unsafe_precondition; use super::ParseBoolError; diff --git a/library/core/src/ub_checks.rs b/library/core/src/ub_checks.rs new file mode 100644 index 00000000000..ff6b2d30539 --- /dev/null +++ b/library/core/src/ub_checks.rs @@ -0,0 +1,158 @@ +//! Provides the [`assert_unsafe_precondition`] macro as well as some utility functions that cover +//! common preconditions. + +use crate::intrinsics::{self, const_eval_select}; + +/// Check that the preconditions of an unsafe function are followed. The check is enabled at +/// runtime if debug assertions are enabled when the caller is monomorphized. In const-eval/Miri +/// checks implemented with this macro for language UB are always ignored. +/// +/// This macro should be called as +/// `assert_unsafe_precondition!(check_{library,lang}_ub, "message", (ident: type = expr, ident: type = expr) => check_expr)` +/// where each `expr` will be evaluated and passed in as function argument `ident: type`. Then all +/// those arguments are passed to a function with the body `check_expr`. +/// Pick `check_language_ub` when this is guarding a violation of language UB, i.e., immediate UB +/// according to the Rust Abstract Machine. Pick `check_library_ub` when this is guarding a violation +/// of a documented library precondition that does not *immediately* lead to language UB. +/// +/// If `check_library_ub` is used but the check is actually guarding language UB, the check will +/// slow down const-eval/Miri and we'll get the panic message instead of the interpreter's nice +/// diagnostic, but our ability to detect UB is unchanged. +/// But if `check_language_ub` is used when the check is actually for library UB, the check is +/// omitted in const-eval/Miri and thus if we eventually execute language UB which relies on the +/// library UB, the backtrace Miri reports may be far removed from original cause. +/// +/// These checks are behind a condition which is evaluated at codegen time, not expansion time like +/// [`debug_assert`]. This means that a standard library built with optimizations and debug +/// assertions disabled will have these checks optimized out of its monomorphizations, but if a +/// caller of the standard library has debug assertions enabled and monomorphizes an expansion of +/// this macro, that monomorphization will contain the check. +/// +/// Since these checks cannot be optimized out in MIR, some care must be taken in both call and +/// implementation to mitigate their compile-time overhead. Calls to this macro always expand to +/// this structure: +/// ```ignore (pseudocode) +/// if ::core::intrinsics::check_language_ub() { +/// precondition_check(args) +/// } +/// ``` +/// where `precondition_check` is monomorphic with the attributes `#[rustc_nounwind]`, `#[inline]` and +/// `#[rustc_no_mir_inline]`. This combination of attributes ensures that the actual check logic is +/// compiled only once and generates a minimal amount of IR because the check cannot be inlined in +/// MIR, but *can* be inlined and fully optimized by a codegen backend. +/// +/// Callers should avoid introducing any other `let` bindings or any code outside this macro in +/// order to call it. Since the precompiled standard library is built with full debuginfo and these +/// variables cannot be optimized out in MIR, an innocent-looking `let` can produce enough +/// debuginfo to have a measurable compile-time impact on debug builds. +#[allow_internal_unstable(const_ub_checks)] // permit this to be called in stably-const fn +macro_rules! assert_unsafe_precondition { + ($kind:ident, $message:expr, ($($name:ident:$ty:ty = $arg:expr),*$(,)?) => $e:expr $(,)?) => { + { + // This check is inlineable, but not by the MIR inliner. + // The reason for this is that the MIR inliner is in an exceptionally bad position + // to think about whether or not to inline this. In MIR, this call is gated behind `debug_assertions`, + // which will codegen to `false` in release builds. Inlining the check would be wasted work in that case and + // would be bad for compile times. + // + // LLVM on the other hand sees the constant branch, so if it's `false`, it can immediately delete it without + // inlining the check. If it's `true`, it can inline it and get significantly better performance. + #[rustc_no_mir_inline] + #[inline] + #[rustc_nounwind] + #[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] + const fn precondition_check($($name:$ty),*) { + if !$e { + ::core::panicking::panic_nounwind( + concat!("unsafe precondition(s) violated: ", $message) + ); + } + } + + if ::core::ub_checks::$kind() { + precondition_check($($arg,)*); + } + } + }; +} +pub(crate) use assert_unsafe_precondition; + +/// Checking library UB is always enabled when UB-checking is done +/// (and we use a reexport so that there is no unnecessary wrapper function). +pub(crate) use intrinsics::ub_checks as check_library_ub; + +/// Determines whether we should check for language UB. +/// +/// The intention is to not do that when running in the interpreter, as that one has its own +/// language UB checks which generally produce better errors. +#[rustc_const_unstable(feature = "const_ub_checks", issue = "none")] +#[inline] +pub(crate) const fn check_language_ub() -> bool { + #[inline] + fn runtime() -> bool { + // Disable UB checks in Miri. + !cfg!(miri) + } + + #[inline] + const fn comptime() -> bool { + // Always disable UB checks. + false + } + + // Only used for UB checks so we may const_eval_select. + intrinsics::ub_checks() && const_eval_select((), comptime, runtime) +} + +/// Checks whether `ptr` is properly aligned with respect to +/// `align_of::<T>()`. +/// +/// In `const` this is approximate and can fail spuriously. It is primarily intended +/// for `assert_unsafe_precondition!` with `check_language_ub`, in which case the +/// check is anyway not executed in `const`. +#[inline] +pub(crate) const fn is_aligned_and_not_null(ptr: *const (), align: usize) -> bool { + !ptr.is_null() && ptr.is_aligned_to(align) +} + +#[inline] +pub(crate) const fn is_valid_allocation_size(size: usize, len: usize) -> bool { + let max_len = if size == 0 { usize::MAX } else { isize::MAX as usize / size }; + len <= max_len +} + +/// Checks whether the regions of memory starting at `src` and `dst` of size +/// `count * size` do *not* overlap. +/// +/// Note that in const-eval this function just returns `true` and therefore must +/// only be used with `assert_unsafe_precondition!`, similar to `is_aligned_and_not_null`. +#[inline] +pub(crate) const fn is_nonoverlapping( + src: *const (), + dst: *const (), + size: usize, + count: usize, +) -> bool { + #[inline] + fn runtime(src: *const (), dst: *const (), size: usize, count: usize) -> bool { + let src_usize = src.addr(); + let dst_usize = dst.addr(); + let Some(size) = size.checked_mul(count) else { + crate::panicking::panic_nounwind( + "is_nonoverlapping: `size_of::<T>() * count` overflows a usize", + ) + }; + let diff = src_usize.abs_diff(dst_usize); + // If the absolute distance between the ptrs is at least as big as the size of the buffer, + // they do not overlap. + diff >= size + } + + #[inline] + const fn comptime(_: *const (), _: *const (), _: usize, _: usize) -> bool { + true + } + + // This is just for safety checks so we can const_eval_select. + const_eval_select((src, dst, size, count), comptime, runtime) +} diff --git a/library/portable-simd/.github/workflows/ci.yml b/library/portable-simd/.github/workflows/ci.yml index 90543044ea8..b292be2d6f9 100644 --- a/library/portable-simd/.github/workflows/ci.yml +++ b/library/portable-simd/.github/workflows/ci.yml @@ -141,6 +141,11 @@ jobs: - name: Test (release) run: cargo test --verbose --target=${{ matrix.target }} --release + - name: Generate docs + run: cargo doc --verbose --target=${{ matrix.target }} + env: + RUSTDOCFLAGS: -Dwarnings + wasm-tests: name: "wasm (firefox, ${{ matrix.name }})" runs-on: ubuntu-latest diff --git a/library/portable-simd/Cargo.lock b/library/portable-simd/Cargo.lock index 46312c09657..1584c704fb2 100644 --- a/library/portable-simd/Cargo.lock +++ b/library/portable-simd/Cargo.lock @@ -177,6 +177,9 @@ name = "std_float" version = "0.1.0" dependencies = [ "core_simd", + "test_helpers", + "wasm-bindgen", + "wasm-bindgen-test", ] [[package]] diff --git a/library/portable-simd/crates/core_simd/src/lib.rs b/library/portable-simd/crates/core_simd/src/lib.rs index a25723e11ce..7a161b7e01d 100644 --- a/library/portable-simd/crates/core_simd/src/lib.rs +++ b/library/portable-simd/crates/core_simd/src/lib.rs @@ -13,11 +13,12 @@ simd_ffi, staged_api, strict_provenance, + prelude_import, ptr_metadata )] #![cfg_attr( all( - any(target_arch = "aarch64", target_arch = "arm",), + any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",), any( all(target_feature = "v6", not(target_feature = "mclass")), all(target_feature = "mclass", target_feature = "dsp"), @@ -33,12 +34,21 @@ any(target_arch = "powerpc", target_arch = "powerpc64"), feature(stdarch_powerpc) )] +#![cfg_attr( + all(target_arch = "x86_64", target_feature = "avx512f"), + feature(stdarch_x86_avx512) +)] #![warn(missing_docs, clippy::missing_inline_in_public_items)] // basically all items, really #![deny(unsafe_op_in_unsafe_fn, clippy::undocumented_unsafe_blocks)] +#![doc(test(attr(deny(warnings))))] #![allow(internal_features)] #![unstable(feature = "portable_simd", issue = "86656")] //! Portable SIMD module. +#[prelude_import] +#[allow(unused_imports)] +use core::prelude::v1::*; + #[path = "mod.rs"] mod core_simd; pub use self::core_simd::simd; diff --git a/library/portable-simd/crates/core_simd/src/masks.rs b/library/portable-simd/crates/core_simd/src/masks.rs index e480c25a51e..e6e27c76a5e 100644 --- a/library/portable-simd/crates/core_simd/src/masks.rs +++ b/library/portable-simd/crates/core_simd/src/masks.rs @@ -34,6 +34,7 @@ mod sealed { fn eq(self, other: Self) -> bool; fn to_usize(self) -> usize; + fn max_unsigned() -> u64; type Unsigned: SimdElement; @@ -78,6 +79,11 @@ macro_rules! impl_element { self as usize } + #[inline] + fn max_unsigned() -> u64 { + <$unsigned>::MAX as u64 + } + type Unsigned = $unsigned; const TRUE: Self = -1; diff --git a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs index ae9ff6894b0..8a1079042f0 100644 --- a/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/src/swizzle_dyn.rs @@ -16,7 +16,10 @@ where #[inline] pub fn swizzle_dyn(self, idxs: Simd<u8, N>) -> Self { #![allow(unused_imports, unused_unsafe)] - #[cfg(all(target_arch = "aarch64", target_endian = "little"))] + #[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" + ))] use core::arch::aarch64::{uint8x8_t, vqtbl1q_u8, vtbl1_u8}; #[cfg(all( target_arch = "arm", @@ -37,6 +40,7 @@ where #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7") ), target_feature = "neon", @@ -48,7 +52,7 @@ where #[cfg(target_feature = "simd128")] 16 => transize(wasm::i8x16_swizzle, self, idxs), #[cfg(all( - target_arch = "aarch64", + any(target_arch = "aarch64", target_arch = "arm64ec"), target_feature = "neon", target_endian = "little" ))] diff --git a/library/portable-simd/crates/core_simd/src/vector.rs b/library/portable-simd/crates/core_simd/src/vector.rs index 9e97a3161bb..6c8205b112c 100644 --- a/library/portable-simd/crates/core_simd/src/vector.rs +++ b/library/portable-simd/crates/core_simd/src/vector.rs @@ -1,5 +1,6 @@ use crate::simd::{ cmp::SimdPartialOrd, + num::SimdUint, ptr::{SimdConstPtr, SimdMutPtr}, LaneCount, Mask, MaskElement, SupportedLaneCount, Swizzle, }; @@ -262,6 +263,7 @@ where /// # Panics /// /// Panics if the slice's length is less than the vector's `Simd::N`. + /// Use `load_or_default` for an alternative that does not panic. /// /// # Example /// @@ -315,6 +317,143 @@ where unsafe { self.store(slice.as_mut_ptr().cast()) } } + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the default value for the element type is returned. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec<i32> = vec![10, 11]; + /// + /// let result = Simd::<i32, 4>::load_or_default(&vec); + /// assert_eq!(result, Simd::from_array([10, 11, 0, 0])); + /// ``` + #[must_use] + #[inline] + pub fn load_or_default(slice: &[T]) -> Self + where + T: Default, + { + Self::load_or(slice, Default::default()) + } + + /// Reads contiguous elements from `slice`. Elements are read so long as they're in-bounds for + /// the `slice`. Otherwise, the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::Simd; + /// let vec: Vec<i32> = vec![10, 11]; + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_or(&vec, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, -2])); + /// ``` + #[must_use] + #[inline] + pub fn load_or(slice: &[T], or: Self) -> Self { + Self::load_select(slice, Mask::splat(true), or) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select_or_default(slice: &[T], enable: Mask<<T as SimdElement>::Mask, N>) -> Self + where + T: Default, + { + Self::load_select(slice, enable, Default::default()) + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled or out of bounds for the slice, that memory location + /// is not accessed and the corresponding value from `or` is passed through. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let vec: Vec<i32> = vec![10, 11, 12, 13, 14, 15, 16, 17, 18]; + /// let enable = Mask::from_array([true, true, false, true]); + /// let or = Simd::from_array([-5, -4, -3, -2]); + /// + /// let result = Simd::load_select(&vec, enable, or); + /// assert_eq!(result, Simd::from_array([10, 11, -3, 13])); + /// ``` + #[must_use] + #[inline] + pub fn load_select( + slice: &[T], + mut enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { Self::load_select_ptr(slice.as_ptr(), enable, or) } + } + + /// Reads contiguous elements from `slice`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_unchecked( + slice: &[T], + enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + let ptr = slice.as_ptr(); + // SAFETY: The safety of reading elements from `slice` is ensured by the caller. + unsafe { Self::load_select_ptr(ptr, enable, or) } + } + + /// Reads contiguous elements starting at `ptr`. Each element is read from memory if its + /// corresponding element in `enable` is `true`. + /// + /// When the element is disabled, that memory location is not accessed and the corresponding + /// value from `or` is passed through. + #[must_use] + #[inline] + pub unsafe fn load_select_ptr( + ptr: *const T, + enable: Mask<<T as SimdElement>::Mask, N>, + or: Self, + ) -> Self { + // SAFETY: The safety of reading elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_load(enable.to_int(), ptr, or) } + } + /// Reads from potentially discontiguous indices in `slice` to construct a SIMD vector. /// If an index is out-of-bounds, the element is instead selected from the `or` vector. /// @@ -493,6 +632,77 @@ where unsafe { core::intrinsics::simd::simd_gather(or, source, enable.to_int()) } } + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written, as long as they're in-bounds of the `slice`. + /// If the element is disabled or out of bounds, no memory access to that location + /// is made. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// write.store_select(&mut arr[..3], enable); + /// assert_eq!(arr, [0, -4, -3, 0]); + /// ``` + #[inline] + pub fn store_select(self, slice: &mut [T], mut enable: Mask<<T as SimdElement>::Mask, N>) { + enable &= mask_up_to(slice.len()); + // SAFETY: We performed the bounds check by updating the mask. &[T] is properly aligned to + // the element. + unsafe { self.store_select_ptr(slice.as_mut_ptr(), enable) } + } + + /// Conditionally write contiguous elements to `slice`. The `enable` mask controls + /// which elements are written. + /// + /// # Safety + /// + /// Every enabled element must be in bounds for the `slice`. + /// + /// # Examples + /// ``` + /// # #![feature(portable_simd)] + /// # #[cfg(feature = "as_crate")] use core_simd::simd; + /// # #[cfg(not(feature = "as_crate"))] use core::simd; + /// # use simd::{Simd, Mask}; + /// let mut arr = [0i32; 4]; + /// let write = Simd::from_array([-5, -4, -3, -2]); + /// let enable = Mask::from_array([false, true, true, true]); + /// + /// unsafe { write.store_select_unchecked(&mut arr, enable) }; + /// assert_eq!(arr, [0, -4, -3, -2]); + /// ``` + #[inline] + pub unsafe fn store_select_unchecked( + self, + slice: &mut [T], + enable: Mask<<T as SimdElement>::Mask, N>, + ) { + let ptr = slice.as_mut_ptr(); + // SAFETY: The safety of writing elements in `slice` is ensured by the caller. + unsafe { self.store_select_ptr(ptr, enable) } + } + + /// Conditionally write contiguous elements starting from `ptr`. + /// The `enable` mask controls which elements are written. + /// When disabled, the memory location corresponding to that element is not accessed. + /// + /// # Safety + /// + /// Memory addresses for element are calculated [`pointer::wrapping_offset`] and + /// each enabled element must satisfy the same conditions as [`core::ptr::write`]. + #[inline] + pub unsafe fn store_select_ptr(self, ptr: *mut T, enable: Mask<<T as SimdElement>::Mask, N>) { + // SAFETY: The safety of writing elements through `ptr` is ensured by the caller. + unsafe { core::intrinsics::simd::simd_masked_store(enable.to_int(), ptr, self) } + } + /// Writes the values in a SIMD vector to potentially discontiguous indices in `slice`. /// If an index is out-of-bounds, the write is suppressed without panicking. /// If two elements in the scattered vector would write to the same index @@ -980,3 +1190,37 @@ where { type Mask = isize; } + +#[inline] +fn lane_indices<const N: usize>() -> Simd<usize, N> +where + LaneCount<N>: SupportedLaneCount, +{ + let mut index = [0; N]; + for i in 0..N { + index[i] = i; + } + Simd::from_array(index) +} + +#[inline] +fn mask_up_to<M, const N: usize>(len: usize) -> Mask<M, N> +where + LaneCount<N>: SupportedLaneCount, + M: MaskElement, +{ + let index = lane_indices::<N>(); + let max_value: u64 = M::max_unsigned(); + macro_rules! case { + ($ty:ty) => { + if N < <$ty>::MAX as usize && max_value as $ty as u64 == max_value { + return index.cast().simd_lt(Simd::splat(len.min(N) as $ty)).cast(); + } + }; + } + case!(u8); + case!(u16); + case!(u32); + case!(u64); + index.simd_lt(Simd::splat(len)).cast() +} diff --git a/library/portable-simd/crates/core_simd/src/vendor.rs b/library/portable-simd/crates/core_simd/src/vendor.rs index 6223bedb4e1..1a34a3a8de5 100644 --- a/library/portable-simd/crates/core_simd/src/vendor.rs +++ b/library/portable-simd/crates/core_simd/src/vendor.rs @@ -24,7 +24,7 @@ mod x86; #[cfg(target_arch = "wasm32")] mod wasm32; -#[cfg(any(target_arch = "aarch64", target_arch = "arm",))] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec", target_arch = "arm",))] mod arm; #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] diff --git a/library/portable-simd/crates/core_simd/src/vendor/arm.rs b/library/portable-simd/crates/core_simd/src/vendor/arm.rs index ee5c6421373..f8878d11f09 100644 --- a/library/portable-simd/crates/core_simd/src/vendor/arm.rs +++ b/library/portable-simd/crates/core_simd/src/vendor/arm.rs @@ -4,12 +4,13 @@ use crate::simd::*; #[cfg(target_arch = "arm")] use core::arch::arm::*; -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "arm64ec"))] use core::arch::aarch64::*; #[cfg(all( any( target_arch = "aarch64", + target_arch = "arm64ec", all(target_arch = "arm", target_feature = "v7"), ), target_endian = "little" @@ -69,7 +70,10 @@ mod simd32 { from_transmute! { unsafe Simd<i8, 4> => int8x4_t } } -#[cfg(target_arch = "aarch64")] +#[cfg(all( + any(target_arch = "aarch64", target_arch = "arm64ec"), + target_endian = "little" +))] mod aarch64 { use super::neon::*; use super::*; diff --git a/library/portable-simd/crates/core_simd/tests/masked_load_store.rs b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs new file mode 100644 index 00000000000..3d38658e945 --- /dev/null +++ b/library/portable-simd/crates/core_simd/tests/masked_load_store.rs @@ -0,0 +1,35 @@ +#![feature(portable_simd)] +use core_simd::simd::prelude::*; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen_test::*; + +#[cfg(target_arch = "wasm32")] +wasm_bindgen_test_configure!(run_in_browser); + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn masked_load_store() { + let mut arr = [u8::MAX; 7]; + + u8x4::splat(0).store_select(&mut arr[5..], Mask::from_array([false, true, false, true])); + // write to index 8 is OOB and dropped + assert_eq!(arr, [255u8, 255, 255, 255, 255, 255, 0]); + + u8x4::from_array([0, 1, 2, 3]).store_select(&mut arr[1..], Mask::splat(true)); + assert_eq!(arr, [255u8, 0, 1, 2, 3, 255, 0]); + + // read from index 8 is OOB and dropped + assert_eq!( + u8x4::load_or(&arr[4..], u8x4::splat(42)), + u8x4::from_array([3, 255, 0, 42]) + ); + assert_eq!( + u8x4::load_select( + &arr[4..], + Mask::from_array([true, false, true, true]), + u8x4::splat(42) + ), + u8x4::from_array([3, 42, 0, 42]) + ); +} diff --git a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs index f21a937f01c..19ffe1417c8 100644 --- a/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs +++ b/library/portable-simd/crates/core_simd/tests/swizzle_dyn.rs @@ -1,6 +1,6 @@ #![feature(portable_simd)] use core::{fmt, ops::RangeInclusive}; -use test_helpers::{self, biteq, make_runner, prop_assert_biteq}; +use test_helpers::{biteq, make_runner, prop_assert_biteq}; fn swizzle_dyn_scalar_ver<const N: usize>(values: [u8; N], idxs: [u8; N]) -> [u8; N] { let mut array = [0; N]; diff --git a/library/portable-simd/crates/std_float/Cargo.toml b/library/portable-simd/crates/std_float/Cargo.toml index 84c69774cbd..0896094ee63 100644 --- a/library/portable-simd/crates/std_float/Cargo.toml +++ b/library/portable-simd/crates/std_float/Cargo.toml @@ -8,6 +8,13 @@ edition = "2021" [dependencies] core_simd = { path = "../core_simd", default-features = false } +[dev-dependencies.test_helpers] +path = "../test_helpers" + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen = "0.2" +wasm-bindgen-test = "0.3" + [features] default = ["as_crate"] as_crate = [] diff --git a/library/portable-simd/crates/std_float/src/lib.rs b/library/portable-simd/crates/std_float/src/lib.rs index 4c547777fde..148aa5f9f17 100644 --- a/library/portable-simd/crates/std_float/src/lib.rs +++ b/library/portable-simd/crates/std_float/src/lib.rs @@ -1,4 +1,3 @@ -#![cfg_attr(feature = "as_crate", no_std)] // We are std! #![cfg_attr( feature = "as_crate", feature(core_intrinsics), @@ -44,7 +43,7 @@ use crate::sealed::Sealed; /// For now this trait is available to permit experimentation with SIMD float /// operations that may lack hardware support, such as `mul_add`. pub trait StdFloat: Sealed + Sized { - /// Fused multiply-add. Computes `(self * a) + b` with only one rounding error, + /// Elementwise fused multiply-add. Computes `(self * a) + b` with only one rounding error, /// yielding a more accurate result than an unfused multiply-add. /// /// Using `mul_add` *may* be more performant than an unfused multiply-add if the target @@ -57,22 +56,65 @@ pub trait StdFloat: Sealed + Sized { unsafe { intrinsics::simd_fma(self, a, b) } } - /// Produces a vector where every lane has the square root value - /// of the equivalently-indexed lane in `self` + /// Produces a vector where every element has the square root value + /// of the equivalently-indexed element in `self` #[inline] #[must_use = "method returns a new vector and does not mutate the original value"] fn sqrt(self) -> Self { unsafe { intrinsics::simd_fsqrt(self) } } - /// Returns the smallest integer greater than or equal to each lane. + /// Produces a vector where every element has the sine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn sin(self) -> Self; + + /// Produces a vector where every element has the cosine of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn cos(self) -> Self; + + /// Produces a vector where every element has the exponential (base e) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp(self) -> Self; + + /// Produces a vector where every element has the exponential (base 2) of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn exp2(self) -> Self; + + /// Produces a vector where every element has the natural logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn ln(self) -> Self; + + /// Produces a vector where every element has the logarithm with respect to an arbitrary + /// in the equivalently-indexed elements in `self` and `base`. + #[inline] + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log(self, base: Self) -> Self { + unsafe { intrinsics::simd_div(self.ln(), base.ln()) } + } + + /// Produces a vector where every element has the base-2 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log2(self) -> Self; + + /// Produces a vector where every element has the base-10 logarithm of the value + /// in the equivalently-indexed element in `self`. + #[must_use = "method returns a new vector and does not mutate the original value"] + fn log10(self) -> Self; + + /// Returns the smallest integer greater than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn ceil(self) -> Self { unsafe { intrinsics::simd_ceil(self) } } - /// Returns the largest integer value less than or equal to each lane. + /// Returns the largest integer value less than or equal to each element. #[must_use = "method returns a new vector and does not mutate the original value"] #[inline] fn floor(self) -> Self { @@ -101,46 +143,65 @@ pub trait StdFloat: Sealed + Sized { impl<const N: usize> Sealed for Simd<f32, N> where LaneCount<N>: SupportedLaneCount {} impl<const N: usize> Sealed for Simd<f64, N> where LaneCount<N>: SupportedLaneCount {} -// We can safely just use all the defaults. -impl<const N: usize> StdFloat for Simd<f32, N> -where - LaneCount<N>: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() - } -} - -impl<const N: usize> StdFloat for Simd<f64, N> -where - LaneCount<N>: SupportedLaneCount, -{ - /// Returns the floating point's fractional value, with its integer part removed. - #[must_use = "method returns a new vector and does not mutate the original value"] - #[inline] - fn fract(self) -> Self { - self - self.trunc() +macro_rules! impl_float { + { + $($fn:ident: $intrinsic:ident,)* + } => { + impl<const N: usize> StdFloat for Simd<f32, N> + where + LaneCount<N>: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + unsafe { intrinsics::$intrinsic(self) } + } + )* + } + + impl<const N: usize> StdFloat for Simd<f64, N> + where + LaneCount<N>: SupportedLaneCount, + { + #[inline] + fn fract(self) -> Self { + self - self.trunc() + } + + $( + #[inline] + fn $fn(self) -> Self { + // https://github.com/llvm/llvm-project/issues/83729 + #[cfg(target_arch = "aarch64")] + { + let mut ln = Self::splat(0f64); + for i in 0..N { + ln[i] = self[i].$fn() + } + ln + } + + #[cfg(not(target_arch = "aarch64"))] + { + unsafe { intrinsics::$intrinsic(self) } + } + } + )* + } } } -#[cfg(test)] -mod tests { - use super::*; - use simd::prelude::*; - - #[test] - fn everything_works() { - let x = f32x4::from_array([0.1, 0.5, 0.6, -1.5]); - let x2 = x + x; - let _xc = x.ceil(); - let _xf = x.floor(); - let _xr = x.round(); - let _xt = x.trunc(); - let _xfma = x.mul_add(x, x); - let _xsqrt = x.sqrt(); - let _ = x2.abs() * x2; - } +impl_float! { + sin: simd_fsin, + cos: simd_fcos, + exp: simd_fexp, + exp2: simd_fexp2, + ln: simd_flog, + log2: simd_flog2, + log10: simd_flog10, } diff --git a/library/portable-simd/crates/std_float/tests/float.rs b/library/portable-simd/crates/std_float/tests/float.rs new file mode 100644 index 00000000000..c66c968f8c6 --- /dev/null +++ b/library/portable-simd/crates/std_float/tests/float.rs @@ -0,0 +1,74 @@ +#![feature(portable_simd)] + +macro_rules! unary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_unary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_| true, + ) + } + )* + } + } +} + +macro_rules! binary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_binary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _| true, + ) + } + )* + } + } +} + +macro_rules! ternary_test { + { $scalar:tt, $($func:tt),+ } => { + test_helpers::test_lanes! { + $( + fn $func<const LANES: usize>() { + test_helpers::test_ternary_elementwise( + &core_simd::simd::Simd::<$scalar, LANES>::$func, + &$scalar::$func, + &|_, _, _| true, + ) + } + )* + } + } +} + +macro_rules! impl_tests { + { $scalar:tt } => { + mod $scalar { + use std_float::StdFloat; + + unary_test! { $scalar, sqrt, sin, cos, exp, exp2, ln, log2, log10, ceil, floor, round, trunc } + binary_test! { $scalar, log } + ternary_test! { $scalar, mul_add } + + test_helpers::test_lanes! { + fn fract<const LANES: usize>() { + test_helpers::test_unary_elementwise_flush_subnormals( + &core_simd::simd::Simd::<$scalar, LANES>::fract, + &$scalar::fract, + &|_| true, + ) + } + } + } + } +} + +impl_tests! { f32 } +impl_tests! { f64 } diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b0bcab7994c..e6e1d32fa54 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -746,7 +746,13 @@ fn rust_panic_with_hook( panic_count::MustAbort::PanicInHook => { // Don't try to print the message in this case // - perhaps that is causing the recursive panics. - rtprintpanic!("thread panicked while processing panic. aborting.\n"); + let panicinfo = PanicInfo::internal_constructor( + None, // no message + location, // but we want to show the location! + can_unwind, + force_no_backtrace, + ); + rtprintpanic!("{panicinfo}\nthread panicked while processing panic. aborting.\n"); } panic_count::MustAbort::AlwaysAbort => { // Unfortunately, this does not print a backtrace, because creating diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index 895fcbd6b7e..d417034f5af 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -396,7 +396,7 @@ impl<T: ?Sized> Mutex<T> { self.poison.get() } - /// Clear the poisoned state from a mutex + /// Clear the poisoned state from a mutex. /// /// If the mutex is poisoned, it will remain poisoned until this function is called. This /// allows recovering from a poisoned state and marking that it has recovered. For example, if diff --git a/library/std/src/sync/rwlock.rs b/library/std/src/sync/rwlock.rs index f7f098c082a..d648cd08994 100644 --- a/library/std/src/sync/rwlock.rs +++ b/library/std/src/sync/rwlock.rs @@ -439,7 +439,7 @@ impl<T: ?Sized> RwLock<T> { self.poison.get() } - /// Clear the poisoned state from a lock + /// Clear the poisoned state from a lock. /// /// If the lock is poisoned, it will remain poisoned until this function is called. This allows /// recovering from a poisoned state and marking that it has recovered. For example, if the diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index ba53ed88f37..23aa4da14a7 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -10,14 +10,16 @@ //! - More information about protocols can be found [here](https://edk2-docs.gitbook.io/edk-ii-uefi-driver-writer-s-guide/3_foundation/36_protocols_and_handles) use r_efi::efi::{self, Guid}; +use r_efi::protocols::{device_path, device_path_to_text}; +use crate::ffi::OsString; +use crate::io::{self, const_io_error}; use crate::mem::{size_of, MaybeUninit}; -use crate::os::uefi; +use crate::os::uefi::{self, env::boot_services, ffi::OsStringExt}; use crate::ptr::NonNull; -use crate::{ - io::{self, const_io_error}, - os::uefi::env::boot_services, -}; +use crate::slice; +use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sys_common::wstr::WStrUnits; const BOOT_SERVICES_UNAVAILABLE: io::Error = const_io_error!(io::ErrorKind::Other, "Boot Services are no longer available"); @@ -142,9 +144,74 @@ pub(crate) unsafe fn close_event(evt: NonNull<crate::ffi::c_void>) -> io::Result /// Get the Protocol for current system handle. /// Note: Some protocols need to be manually freed. It is the callers responsibility to do so. -pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>> { - let system_handle = uefi::env::try_image_handle()?; - open_protocol(system_handle, protocol_guid).ok() +pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> io::Result<NonNull<T>> { + let system_handle = uefi::env::try_image_handle().ok_or(io::const_io_error!( + io::ErrorKind::NotFound, + "Protocol not found in Image handle" + ))?; + open_protocol(system_handle, protocol_guid) +} + +pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::Result<OsString> { + fn path_to_text( + protocol: NonNull<device_path_to_text::Protocol>, + path: NonNull<device_path::Protocol>, + ) -> io::Result<OsString> { + let path_ptr: *mut r_efi::efi::Char16 = unsafe { + ((*protocol.as_ptr()).convert_device_path_to_text)( + path.as_ptr(), + // DisplayOnly + r_efi::efi::Boolean::FALSE, + // AllowShortcuts + r_efi::efi::Boolean::FALSE, + ) + }; + + // SAFETY: `convert_device_path_to_text` returns a pointer to a null-terminated UTF-16 + // string, and that string cannot be deallocated prior to dropping the `WStrUnits`, so + // it's safe for `WStrUnits` to use. + let path_len = unsafe { + WStrUnits::new(path_ptr) + .ok_or(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid path"))? + .count() + }; + + let path = OsString::from_wide(unsafe { slice::from_raw_parts(path_ptr.cast(), path_len) }); + + if let Some(boot_services) = crate::os::uefi::env::boot_services() { + let boot_services: NonNull<r_efi::efi::BootServices> = boot_services.cast(); + unsafe { + ((*boot_services.as_ptr()).free_pool)(path_ptr.cast()); + } + } + + Ok(path) + } + + static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + AtomicPtr::new(crate::ptr::null_mut()); + + if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + return path_to_text(protocol, path); + } + } + + let device_path_to_text_handles = locate_handles(device_path_to_text::PROTOCOL_GUID)?; + for handle in device_path_to_text_handles { + if let Ok(protocol) = open_protocol::<device_path_to_text::Protocol>( + handle, + device_path_to_text::PROTOCOL_GUID, + ) { + LAST_VALID_HANDLE.store(handle.as_ptr(), Ordering::Release); + return path_to_text(protocol, path); + } + } + + Err(io::const_io_error!(io::ErrorKind::NotFound, "No device path to text protocol found")) } /// Get RuntimeServices diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs index e6693db68e6..58838c5876e 100644 --- a/library/std/src/sys/pal/uefi/os.rs +++ b/library/std/src/sys/pal/uefi/os.rs @@ -1,4 +1,4 @@ -use super::{unsupported, RawOsError}; +use super::{helpers, unsupported, RawOsError}; use crate::error::Error as StdError; use crate::ffi::{OsStr, OsString}; use crate::fmt; @@ -7,6 +7,7 @@ use crate::marker::PhantomData; use crate::os::uefi; use crate::path::{self, PathBuf}; use crate::ptr::NonNull; +use r_efi::efi::protocols::{device_path, loaded_image_device_path}; use r_efi::efi::Status; pub fn errno() -> RawOsError { @@ -164,7 +165,10 @@ impl fmt::Display for JoinPathsError { impl StdError for JoinPathsError {} pub fn current_exe() -> io::Result<PathBuf> { - unsupported() + let protocol = helpers::image_handle_protocol::<device_path::Protocol>( + loaded_image_device_path::PROTOCOL_GUID, + )?; + helpers::device_path_to_text(protocol).map(PathBuf::from) } pub struct Env(!); diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 25bb5938b4c..51d31a00afe 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -4,6 +4,7 @@ #![feature(staged_api)] #![feature(c_unwind)] #![feature(strict_provenance)] +#![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] #![cfg_attr(not(target_env = "msvc"), feature(libc))] #![cfg_attr( all(target_family = "wasm", not(target_os = "emscripten")), diff --git a/library/unwind/src/wasm.rs b/library/unwind/src/wasm.rs index b06671bcb83..f4ffac1ba16 100644 --- a/library/unwind/src/wasm.rs +++ b/library/unwind/src/wasm.rs @@ -59,7 +59,10 @@ pub unsafe fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwi wasm_throw(0, exception.cast()) } else { let _ = exception; - core::arch::wasm32::unreachable() + #[cfg(target_arch = "wasm32")] + core::arch::wasm32::unreachable(); + #[cfg(target_arch = "wasm64")] + core::arch::wasm64::unreachable(); } } } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c6eb7be08cd..211921715b0 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -20,9 +20,9 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::{Target, TargetTriple}; -use tempfile::Builder as TempFileBuilder; use std::env; +use std::fs::File; use std::io::{self, Write}; use std::panic; use std::path::{Path, PathBuf}; @@ -31,6 +31,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use tempfile::{Builder as TempFileBuilder, TempDir}; + use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; @@ -48,7 +50,55 @@ pub(crate) struct GlobalTestOptions { pub(crate) attrs: Vec<String>, } -pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { +pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { + let mut file = File::create(file_path) + .map_err(|error| format!("failed to create args file: {error:?}"))?; + + // We now put the common arguments into the file we created. + let mut content = vec!["--crate-type=bin".to_string()]; + + for cfg in &options.cfgs { + content.push(format!("--cfg={cfg}")); + } + if !options.check_cfgs.is_empty() { + content.push("-Zunstable-options".to_string()); + for check_cfg in &options.check_cfgs { + content.push(format!("--check-cfg={check_cfg}")); + } + } + + if let Some(sysroot) = &options.maybe_sysroot { + content.push(format!("--sysroot={}", sysroot.display())); + } + for lib_str in &options.lib_strs { + content.push(format!("-L{lib_str}")); + } + for extern_str in &options.extern_strs { + content.push(format!("--extern={extern_str}")); + } + content.push("-Ccodegen-units=1".to_string()); + for codegen_options_str in &options.codegen_options_strs { + content.push(format!("-C{codegen_options_str}")); + } + for unstable_option_str in &options.unstable_opts_strs { + content.push(format!("-Z{unstable_option_str}")); + } + + let content = content.join("\n"); + + file.write(content.as_bytes()) + .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; + Ok(()) +} + +fn get_doctest_dir() -> io::Result<TempDir> { + TempFileBuilder::new().prefix("rustdoctest").tempdir() +} + +pub(crate) fn run( + dcx: &rustc_errors::DiagCtxt, + options: RustdocOptions, +) -> Result<(), ErrorGuaranteed> { let input = config::Input::File(options.input.clone()); let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -118,6 +168,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; + let temp_dir = match get_doctest_dir() + .map_err(|error| format!("failed to create temporary directory: {error:?}")) + { + Ok(temp_dir) => temp_dir, + Err(error) => return crate::wrap_return(dcx, Err(error)), + }; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { @@ -134,6 +193,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { Some(compiler.sess.psess.clone_source_map()), None, enable_per_target_ignores, + file_path, ); let mut hir_collector = HirCollector { @@ -322,44 +382,35 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: RustdocOptions, + rustdoc_options: IndividualTestOptions, mut lang_string: LangString, no_run: bool, - runtool: Option<String>, - runtool_args: Vec<String>, - target: TargetTriple, opts: &GlobalTestOptions, edition: Edition, - outdir: DirState, path: PathBuf, - test_id: &str, report_unused_externs: impl Fn(UnusedExterns), ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = - make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); + let (test, line_offset, supports_color) = make_test( + test, + Some(crate_name), + lang_string.test_harness, + opts, + edition, + Some(&rustdoc_options.test_id), + ); // Make sure we emit well-formed executable names for our target. - let rust_out = add_exe_suffix("rust_out".to_owned(), &target); - let output_file = outdir.path().join(rust_out); + let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); + let output_file = rustdoc_options.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg("--crate-type").arg("bin"); - for cfg in &rustdoc_options.cfgs { - compiler.arg("--cfg").arg(&cfg); - } - if !rustdoc_options.check_cfgs.is_empty() { - compiler.arg("-Z").arg("unstable-options"); - for check_cfg in &rustdoc_options.check_cfgs { - compiler.arg("--check-cfg").arg(&check_cfg); - } - } - if let Some(sysroot) = rustdoc_options.maybe_sysroot { - compiler.arg("--sysroot").arg(sysroot); - } + + compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); + compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); @@ -367,29 +418,17 @@ fn run_test( if lang_string.test_harness { compiler.arg("--test"); } - if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { + if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); - compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); + compiler.arg("-Z").arg("unstable-options"); } - for lib_str in &rustdoc_options.lib_strs { - compiler.arg("-L").arg(&lib_str); - } - for extern_str in &rustdoc_options.extern_strs { - compiler.arg("--extern").arg(&extern_str); - } - compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &rustdoc_options.codegen_options_strs { - compiler.arg("-C").arg(&codegen_options_str); - } - for unstable_option_str in &rustdoc_options.unstable_opts_strs { - compiler.arg("-Z").arg(&unstable_option_str); - } - if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + + if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match target { + compiler.arg("--target").arg(match rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() @@ -485,10 +524,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = runtool { + if let Some(tool) = rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(runtool_args); + cmd.args(rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); @@ -897,6 +936,56 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { (before, after, crates) } +pub(crate) struct IndividualTestOptions { + test_builder: Option<PathBuf>, + test_builder_wrappers: Vec<PathBuf>, + is_json_unused_externs_enabled: bool, + should_persist_doctests: bool, + error_format: ErrorOutputType, + test_run_directory: Option<PathBuf>, + nocapture: bool, + arg_file: PathBuf, + outdir: DirState, + runtool: Option<String>, + runtool_args: Vec<String>, + target: TargetTriple, + test_id: String, +} + +impl IndividualTestOptions { + fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + let outdir = if let Some(ref path) = options.persist_doctests { + let mut path = path.clone(); + path.push(&test_id); + + if let Err(err) = std::fs::create_dir_all(&path) { + eprintln!("Couldn't create directory for doctest executables: {err}"); + panic::resume_unwind(Box::new(())); + } + + DirState::Perm(path) + } else { + DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) + }; + + Self { + test_builder: options.test_builder.clone(), + test_builder_wrappers: options.test_builder_wrappers.clone(), + is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), + should_persist_doctests: options.persist_doctests.is_none(), + error_format: options.error_format, + test_run_directory: options.test_run_directory.clone(), + nocapture: options.nocapture, + arg_file: arg_file.into(), + outdir, + runtool: options.runtool.clone(), + runtool_args: options.runtool_args.clone(), + target: options.target.clone(), + test_id, + } + } +} + pub(crate) trait Tester { fn add_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { @@ -941,6 +1030,7 @@ pub(crate) struct Collector { visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc<Mutex<Vec<UnusedExterns>>>, compiling_test_count: AtomicUsize, + arg_file: PathBuf, } impl Collector { @@ -952,6 +1042,7 @@ impl Collector { source_map: Option<Lrc<SourceMap>>, filename: Option<PathBuf>, enable_per_target_ignores: bool, + arg_file: PathBuf, ) -> Collector { Collector { tests: Vec::new(), @@ -967,6 +1058,7 @@ impl Collector { visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), + arg_file, } } @@ -1009,13 +1101,9 @@ impl Tester for Collector { let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); - let rustdoc_options = self.rustdoc_options.clone(); - let runtool = self.rustdoc_options.runtool.clone(); - let runtool_args = self.rustdoc_options.runtool_args.clone(); - let target = self.rustdoc_options.target.clone(); - let target_str = target.to_string(); + let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || rustdoc_options.no_run; + let no_run = config.no_run || self.rustdoc_options.no_run; if !config.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1049,23 +1137,9 @@ impl Tester for Collector { self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) }, ); - let outdir = if let Some(mut path) = rustdoc_options.persist_doctests.clone() { - path.push(&test_id); - if let Err(err) = std::fs::create_dir_all(&path) { - eprintln!("Couldn't create directory for doctest executables: {err}"); - panic::resume_unwind(Box::new(())); - } - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; + let rustdoc_test_options = + IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { @@ -1096,17 +1170,12 @@ impl Tester for Collector { &test, &crate_name, line, - rustdoc_options, + rustdoc_test_options, config, no_run, - runtool, - runtool_args, - target, &opts, edition, - outdir, path, - &test_id, report_unused_externs, ); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 18651875130..b78fb4c4eee 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -664,7 +664,7 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { +pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { match res { Ok(()) => dcx.has_errors().map_or(Ok(()), Err), Err(err) => Err(dcx.err(err)), @@ -731,7 +731,7 @@ fn main_args( match (options.should_test, options.markdown_input()) { (true, true) => return wrap_return(&diag, markdown::test(options)), - (true, false) => return doctest::run(options), + (true, false) => return doctest::run(&diag, options), (false, true) => { let input = options.input.clone(); let edition = options.edition; diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index b661ced0169..dcd2cf02a30 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -3,11 +3,13 @@ use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; use std::path::Path; +use tempfile::tempdir; + use rustc_span::edition::Edition; use rustc_span::DUMMY_SP; use crate::config::{Options, RenderOptions}; -use crate::doctest::{Collector, GlobalTestOptions}; +use crate::doctest::{generate_args_file, Collector, GlobalTestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{ @@ -146,6 +148,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> { .map_err(|err| format!("{input}: {err}", input = options.input.display()))?; let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; + + let temp_dir = + tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&file_path, &options)?; + let mut collector = Collector::new( options.input.display().to_string(), options.clone(), @@ -154,6 +162,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, Some(options.input), options.enable_per_target_ignores, + file_path, ); collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 88d9f762a87..8e27b3ccefd 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -163,7 +163,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC // TODO: This check currently bails if the local variable has no initializer. // That is overly conservative - the lint should fire even if there was no initializer, // but the variable has been initialized before `lhs` was evaluated. - if let Some(Node::Local(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) + if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p)) && local.init.is_none() { return false; diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 66206d1a059..8683cb86e8a 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -6,7 +6,7 @@ use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_ty, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, Local, Node, QPath, Ty, TyKind}; +use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::print::with_forced_trimmed_paths; @@ -139,7 +139,7 @@ impl<'tcx> Visitor<'tcx> for InferVisitor { fn given_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(expr.hir_id) { - Node::Local(Local { ty: Some(ty), .. }) => { + Node::LetStmt(LetStmt { ty: Some(ty), .. }) => { let mut v = InferVisitor::default(); v.visit_ty(ty); !v.0 diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs index 662737a14a4..f42bafce4dd 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( && let ty::RawPtr(_, to_mutbl) = cast_to.kind() && let Some(use_cx) = expr_use_ctxt(cx, expr) // TODO: only block the lint if `cast_expr` is a temporary - && !matches!(use_cx.node, ExprUseNode::Local(_) | ExprUseNode::ConstStatic(_)) + && !matches!(use_cx.node, ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_)) { let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; let fn_name = match to_mutbl { diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 08341ff32f3..148d52cb5dd 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -66,7 +66,7 @@ pub(super) fn check<'tcx>( && let QPath::Resolved(None, Path { res, .. }) = qpath && let Res::Local(hir_id) = res && let parent = cx.tcx.parent_hir_node(*hir_id) - && let Node::Local(local) = parent + && let Node::LetStmt(local) = parent { if let Some(ty) = local.ty && let TyKind::Path(qpath) = ty.kind @@ -275,7 +275,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx } // Local usage } else if let Res::Local(hir_id) = res - && let Node::Local(l) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(l) = cx.tcx.parent_hir_node(hir_id) { if let Some(e) = l.init && is_cast_from_ty_alias(cx, e, cast_from) diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index d820413e111..e921b9b46a6 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::for_each_expr_with_closures; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; -use rustc_hir::{Block, ExprKind, HirId, LangItem, Local, Node, PatKind}; +use rustc_hir::{Block, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; @@ -58,7 +58,7 @@ static COLLECTIONS: [Symbol; 9] = [ ]; impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { // Look for local variables whose type is a container. Search surrounding bock for read access. if match_acceptable_type(cx, local, &COLLECTIONS) && let PatKind::Binding(_, local_id, _, _) = local.pat.kind @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead { } } -fn match_acceptable_type(cx: &LateContext<'_>, local: &Local<'_>, collections: &[rustc_span::Symbol]) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[rustc_span::Symbol]) -> bool { let ty = cx.typeck_results().pat_ty(local.pat); collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) // String type is a lang item but not a diagnostic item for now so we need a separate check diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs index 80a537b9f94..a32201d8079 100644 --- a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -46,7 +46,7 @@ impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { // Ignore function parameters return; }, - Node::Local(local) if local.ty.is_some() => { + Node::LetStmt(local) if local.ty.is_some() => { // Ignore let bindings with explicit type return; }, diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 0ea53c39280..619e933b4ff 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type}; use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths}; -use rustc_hir::{Local, LocalSource, PatKind}; +use rustc_hir::{LetStmt, LocalSource, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{GenericArgKind, IsSuggestable}; @@ -138,7 +138,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [ ]; impl<'tcx> LateLintPass<'tcx> for LetUnderscore { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if matches!(local.source, LocalSource::Normal) && !in_external_macro(cx.tcx.sess, local.span) && let PatKind::Wild = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 5f3f9b43f45..593b29154b4 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; -use rustc_hir::{Local, TyKind}; +use rustc_hir::{LetStmt, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; @@ -26,7 +26,7 @@ declare_clippy_lint! { declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]); impl LateLintPass<'_> for UnderscoreTyped { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { if !in_external_macro(cx.tcx.sess, local.span) && let Some(ty) = local.ty // Ensure that it has a type defined && let TyKind::Infer = &ty.kind // that type is '_' diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 0f35514b8ad..670a78d58c3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -62,7 +62,7 @@ pub(super) fn check<'tcx>( if let Node::Pat(pat) = node && let PatKind::Binding(bind_ann, ..) = pat.kind && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)) - && let Node::Local(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(parent_let_expr) = cx.tcx.parent_hir_node(hir_id) && let Some(init) = parent_let_expr.init { match init.kind { diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 8bca33754e8..7b45cc95431 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -3,7 +3,7 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_local, Visitor}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, PatKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -141,7 +141,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> { type NestedFilter = nested_filter::OnlyBodies; - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { // Look for declarations of the variable if l.pat.hir_id == self.var_id && let PatKind::Binding(.., ident, _) = l.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs index 93774b89768..bd04827a1f0 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs @@ -5,13 +5,13 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::any_temporaries_need_ordered_drop; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, Local, MatchSource, Pat, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, LetStmt, MatchSource, Pat, StmtKind}; use rustc_lint::LateContext; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) { ([stmt, stmts @ ..], expr) => { - if let StmtKind::Let(&Local { + if let StmtKind::Let(&LetStmt { init: Some(e), els: None, .. diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index d070ee74985..194dd4752f9 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -6,7 +6,7 @@ use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutabl use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, Local, Mutability, PatKind, UnOp}; +use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; @@ -286,7 +286,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: & self.cx.tcx.hir() } - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { if !self.after_loop { l.pat.each_binding_or_first(&mut |_, id, _, _| { if id == self.local_id { diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 5cbab0ec977..f8f33cfc82e 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt; use clippy_utils::visitors::{is_local_used, local_used_once}; use clippy_utils::{is_trait_method, path_to_local_id}; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, ExprKind, Local, Node, PatKind, StmtKind}; +use rustc_hir::{BindingAnnotation, ExprKind, LetStmt, Node, PatKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym; @@ -60,7 +60,7 @@ impl ManualHashOne { impl_lint_pass!(ManualHashOne => [MANUAL_HASH_ONE]); impl LateLintPass<'_> for ManualHashOne { - fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) { // `let mut hasher = seg.build_hasher();` if let PatKind::Binding(BindingAnnotation::MUT, hasher, _, None) = local.pat.kind && let Some(init) = local.init diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 0bde62bd554..ab9bca170cf 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { // Apply only to params or locals with annotated types match cx.tcx.parent_hir_node(hir_id) { Node::Param(..) => (), - Node::Local(local) => { + Node::LetStmt(local) => { let Some(ty) = local.ty else { return }; if matches!(ty.kind, TyKind::Infer) { return; diff --git a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs index c8a48246e67..0f242e0b9e1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs}; use rustc_errors::Applicability; -use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath}; +use rustc_hir::{ByRef, ExprKind, LetStmt, MatchSource, PatKind, QPath}; use rustc_lint::LateContext; use super::INFALLIBLE_DESTRUCTURING_MATCH; -pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { +pub(crate) fn check(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool { if !local.span.from_expansion() && let Some(expr) = local.init && let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs index 61977045fd4..864923b2773 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs @@ -148,7 +148,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<AssignmentExpr> { if let Node::Expr(parent_arm_expr) = cx.tcx.parent_hir_node(ex.hir_id) { return match cx.tcx.parent_hir_node(parent_arm_expr.hir_id) { - Node::Local(parent_let_expr) => Some(AssignmentExpr::Local { + Node::LetStmt(parent_let_expr) => Some(AssignmentExpr::Local { span: parent_let_expr.span, pat_span: parent_let_expr.pat.span(), }), diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index 50494f4819f..580d4a64296 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -27,7 +27,7 @@ mod wild_in_or_pats; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, tokenize_with_text}; -use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; +use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat}; use rustc_lexer::TokenKind; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -1124,7 +1124,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { self.infallible_destructuring_match_linted |= local.els.is_none() && infallible_destructuring_match::check(cx, local); } diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 3d3f29e5fc6..cee77f62b61 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -125,7 +125,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { match cx.tcx.parent_hir_node(p_expr.hir_id) { // Compare match_expr ty with local in `let local = match match_expr {..}` - Node::Local(local) => { + Node::LetStmt(local) => { let results = cx.typeck_results(); return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr)); }, diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index fb5c0c544a9..d4a5de3d1de 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -69,7 +69,7 @@ pub(super) fn check( _ => false, }, // local binding capturing a reference - Node::Local(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { + Node::LetStmt(l) if matches!(l.pat.kind, PatKind::Binding(BindingAnnotation(ByRef::Yes, _), ..)) => { return; }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 4c7c56e7174..19b7e97339d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -50,7 +50,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re | ExprKind::Break(_, _) => true, _ => false, }, - Some((Node::Stmt(_) | Node::Local(_), _)) => false, + Some((Node::Stmt(_) | Node::LetStmt(_), _)) => false, _ => true, }; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index 78540353005..9e2fd92255e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -11,7 +11,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind, + BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -85,7 +85,7 @@ pub(super) fn check<'tcx>( ); } }, - Node::Local(l) => { + Node::LetStmt(l) => { if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None) = l.pat.kind && let ty = cx.typeck_results().expr_ty(collect_expr) && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList] @@ -424,7 +424,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v> match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)), StmtKind::Item(..) => None, - StmtKind::Let(Local { init, pat, .. }) => { + StmtKind::Let(LetStmt { init, pat, .. }) => { if let PatKind::Binding(_, hir_id, ..) = pat.kind { init.map(|init_expr| (init_expr, Some(hir_id))) } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs index 6c6846c4b47..9b0180d9369 100644 --- a/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/readonly_write_lock.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, receiver && let Node::Expr(unwrap_call_expr) = cx.tcx.parent_hir_node(expr.hir_id) && is_unwrap_call(cx, unwrap_call_expr) && let parent = cx.tcx.parent_hir_node(unwrap_call_expr.hir_id) - && let Node::Local(local) = parent + && let Node::LetStmt(local) = parent && let Some(mir) = enclosing_mir(cx.tcx, expr.hir_id) && let Some((local, _)) = mir .local_decls diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 55cd1a38ec9..946cdb49d27 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -8,7 +8,7 @@ use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths} use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{ - BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, + BindingAnnotation, Expr, ExprKind, HirId, LangItem, LetStmt, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, }; use rustc_lint::LateContext; use rustc_middle::ty; @@ -128,7 +128,7 @@ fn check_manual_split_once_indirect( ) -> Option<()> { let ctxt = expr.span.ctxt(); let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); - if let (_, Node::Local(local)) = parents.next()? + if let (_, Node::LetStmt(local)) = parents.next()? && let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? && let (_, Node::Block(enclosing_block)) = parents.next()? @@ -198,7 +198,7 @@ fn indirect_usage<'tcx>( binding: HirId, ctxt: SyntaxContext, ) -> Option<IndirectUsage<'tcx>> { - if let StmtKind::Let(&Local { + if let StmtKind::Let(&LetStmt { pat: Pat { kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None), .. diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index 988f3e86fcf..ccc8d17970e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -20,7 +20,7 @@ fn needs_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { // some common cases where turbofish isn't needed: // - assigned to a local variable with a type annotation - if let hir::Node::Local(local) = parent + if let hir::Node::LetStmt(local) = parent && local.ty.is_some() { return false; diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 12c7c18afde..656fb907fcd 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note}; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { match stmt.kind { StmtKind::Let(local) => { - if let Local { init: Some(e), .. } = local { + if let LetStmt { init: Some(e), .. } = local { DivergenceVisitor { cx }.visit_expr(e); } }, diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index c32025fcbb6..79f0a398d55 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType { } } - fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) { + fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::LetStmt<'_>) { if let hir::PatKind::Wild = local.pat.kind { return; } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index 4cda4b171e3..810799acb2e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -6,7 +6,7 @@ use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_loca use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -237,7 +237,7 @@ fn first_usage<'tcx>( }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> { +fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<String> { let span = local.span.with_hi(match local.ty { // let <pat>: <ty>; // ~~~~~~~~~~~~~~~ @@ -252,7 +252,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> O fn check<'tcx>( cx: &LateContext<'tcx>, - local: &'tcx Local<'tcx>, + local: &'tcx LetStmt<'tcx>, local_stmt: &'tcx Stmt<'tcx>, block: &'tcx Block<'tcx>, binding_id: HirId, @@ -363,9 +363,9 @@ fn check<'tcx>( } impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if let Local { + if let LetStmt { init: None, pat: &Pat { diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 2587b3881bb..896c99a7104 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -604,7 +604,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: match get_expr_use_or_unification_node(self.cx.tcx, e) { Some((Node::Stmt(_), _)) => (), - Some((Node::Local(l), _)) => { + Some((Node::LetStmt(l), _)) => { // Only trace simple bindings. e.g `let x = y;` if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind { self.bindings.insert(id, args_idx); diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index 831c291ed7c..f1db571e113 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -14,7 +14,7 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ - BindingAnnotation, Block, ByRef, Expr, ExprKind, Local, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, + BindingAnnotation, Block, ByRef, Expr, ExprKind, LetStmt, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; @@ -109,7 +109,7 @@ fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir } fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Let(Local { + if let StmtKind::Let(LetStmt { pat, init: Some(init_expr), els: Some(els), diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index 0ebdb031d5a..7f4735c6a88 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -3,7 +3,7 @@ use clippy_utils::get_enclosing_block; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use hir::{Expr, ExprKind, HirId, Local, PatKind, PathSegment, QPath, StmtKind}; +use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; @@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec { } if let StmtKind::Let(local) = stmt.kind - && let Local { + && let LetStmt { pat, init: Some(init), .. } = local && let PatKind::Binding(_, id, ident, _) = pat.kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 6528a7b369f..0f579f779df 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -3,7 +3,7 @@ use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; use rustc_ast::Mutability; use rustc_hir::def::Res; -use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath}; +use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, LetStmt, Node, Pat, PatKind, QPath}; use rustc_hir_typeck::expr_use_visitor::PlaceBase; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -47,7 +47,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); impl<'tcx> LateLintPass<'tcx> for RedundantLocals { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if !local.span.is_desugaring(DesugaringKind::Async) // the pattern is a single by-value binding && let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 079e6500e3c..96f6f0ec36f 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -131,7 +131,7 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option<hir::PrimTy> { } impl LateLintPass<'_> for RedundantTypeAnnotations { - fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) { + fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::LetStmt<'tcx>) { if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id) // type annotation part && !local.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs index ca7a0c7c87b..c227b5b22f4 100644 --- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::{is_from_proc_macro, path_to_local_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for ReserveAfterInitialization { self.searcher = None; } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if let Some(init_expr) = local.init && let PatKind::Binding(BindingAnnotation::MUT, id, _, None) = local.pat.kind && !in_external_macro(cx.sess(), local.span) diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index df7bd4c8d1d..d98b37bda35 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -241,7 +241,7 @@ fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<' ExprKind::Match(e, _, _) | ExprKind::Let(&LetExpr { init: e, .. }) => Some(e), _ => None, }, - Node::Local(local) => local.init, + Node::LetStmt(local) => local.init, _ => None, }; return init; diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 0d84a9ab395..564b065d0ba 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -159,7 +159,7 @@ fn all_bindings_are_for_conv<'tcx>( .iter() .map(|node| match node { Node::Pat(pat) => kind.eq(&pat.kind).then_some(pat.hir_id), - Node::Local(l) => Some(l.hir_id), + Node::LetStmt(l) => Some(l.hir_id), _ => None, }) .all_equal() @@ -170,7 +170,7 @@ fn all_bindings_are_for_conv<'tcx>( && local_parents.first().is_some_and(|node| { let Some(ty) = match node { Node::Pat(pat) => Some(pat.hir_id), - Node::Local(l) => Some(l.hir_id), + Node::LetStmt(l) => Some(l.hir_id), _ => None, } .map(|hir_id| cx.typeck_results().node_type(hir_id)) else { diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 2ad15ac8312..0802cb2b7c7 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -12,7 +12,7 @@ mod vec_box; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_hir::{ - Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem, + Body, FnDecl, FnRetTy, GenericArg, ImplItem, ImplItemKind, Item, ItemKind, LetStmt, MutTy, QPath, TraitItem, TraitItemKind, TyKind, }; use rustc_lint::{LateContext, LateLintPass}; @@ -425,7 +425,7 @@ impl<'tcx> LateLintPass<'tcx> for Types { } } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { self.check_ty( cx, diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 0efa65b28e2..5fe4b74b3a7 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -158,7 +158,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { - let (hir::StmtKind::Let(&hir::Local { init: Some(expr), .. }) + let (hir::StmtKind::Let(&hir::LetStmt { init: Some(expr), .. }) | hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr)) = stmt.kind else { @@ -342,7 +342,7 @@ fn block_parents_have_safety_comment( ) -> bool { let (span, hir_id) = match cx.tcx.parent_hir_node(id) { Node::Expr(expr) => match cx.tcx.parent_hir_node(expr.hir_id) { - Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), + Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, @@ -358,12 +358,12 @@ fn block_parents_have_safety_comment( }, Node::Stmt(hir::Stmt { kind: - hir::StmtKind::Let(hir::Local { span, hir_id, .. }) + hir::StmtKind::Let(hir::LetStmt { span, hir_id, .. }) | hir::StmtKind::Expr(hir::Expr { span, hir_id, .. }) | hir::StmtKind::Semi(hir::Expr { span, hir_id, .. }), .. }) - | Node::Local(hir::Local { span, hir_id, .. }) => (*span, *hir_id), + | Node::LetStmt(hir::LetStmt { span, hir_id, .. }) => (*span, *hir_id), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), span, @@ -603,7 +603,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> { for (_, node) in map.parent_iter(body.hir_id) { match node { Node::Expr(e) => span = e.span, - Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (), + Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::LetStmt(_) => (), Node::Item(hir::Item { kind: hir::ItemKind::Const(..) | ItemKind::Static(..), .. diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index 9406d160769..ffb909d7907 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -4,14 +4,14 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, MatchSource, Node, PatKind, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_middle::ty; use super::LET_UNIT_VALUE; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) { // skip `let () = { ... }` if let PatKind::Tuple(fields, ..) = local.pat.kind && fields.is_empty() @@ -102,7 +102,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) - return false; } while let Some(id) = locals_to_check.pop() { - if let Node::Local(l) = cx.tcx.parent_hir_node(id) { + if let Node::LetStmt(l) = cx.tcx.parent_hir_node(id) { if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) { return false; } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs index 0abd48e6423..e016bd3434b 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/mod.rs @@ -3,7 +3,7 @@ mod unit_arg; mod unit_cmp; mod utils; -use rustc_hir::{Expr, Local}; +use rustc_hir::{Expr, LetStmt}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; @@ -99,7 +99,7 @@ declare_clippy_lint! { declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]); impl<'tcx> LateLintPass<'tcx> for UnitTypes { - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let_unit_value::check(cx, local); } diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 3f4ab3e31cf..e6f799335d7 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; use rustc_ast::Mutability; use rustc_hir::intravisit::{walk_expr, Visitor}; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind}; +use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_session::declare_lint_pass; @@ -190,7 +190,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> { }, } }, - Node::Local(Local { init: Some(init), .. }) => { + Node::LetStmt(LetStmt { init: Some(init), .. }) => { if arg_is_mut_peekable(self.cx, init) { self.found_peek_call = true; } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 1ebe1d6a2c4..c56c8ddc7a9 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1006,7 +1006,7 @@ fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) - fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { match cx.tcx.parent_hir_node(hir_id) { - hir::Node::Local(local) => Some(local), + hir::Node::LetStmt(local) => Some(local), hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id), _ => None, } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index f4e277fd0c4..304a1379374 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -217,7 +217,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve match peel_hir_expr_refs(expr).0.kind { ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) { Res::Local(hir_id) => { - if let Node::Local(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { + if let Node::LetStmt(Local { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) { path_to_matched_type(cx, init) } else { None diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 7cfcf3fe946..27ead55bf39 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -9,7 +9,7 @@ use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, higher, is_trait_method}; use rustc_errors::Applicability; -use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Local, Mutability, Node, Pat, PatKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, LetStmt, Mutability, Node, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; @@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { match cx.tcx.parent_hir_node(expr.hir_id) { // search for `let foo = vec![_]` expressions where all uses of `foo` // adjust to slices or call a method that exist on slices (e.g. len) - Node::Local(Local { + Node::LetStmt(LetStmt { ty: None, pat: Pat { @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { } }, // if the local pattern has a specified type, do not lint. - Node::Local(Local { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { + Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => { self.span_to_lint_map.insert(callsite, None); }, // search for `for _ in vec![...]` diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index ac3b2bdaf65..b58a4fb8474 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -7,7 +7,7 @@ use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - BindingAnnotation, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, + BindingAnnotation, Block, Expr, ExprKind, HirId, LetStmt, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { self.searcher = None; } - fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { + fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { if let Some(init_expr) = local.init && let PatKind::Binding(BindingAnnotation::MUT, id, name, None) = local.pat.kind && !in_external_macro(cx.sess(), local.span) diff --git a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs index 852d04cd21b..143fecdd237 100644 --- a/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/zero_repeat_side_effects.rs @@ -78,7 +78,7 @@ fn inner_check(cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>, inner_expr: let parent_hir_node = cx.tcx.parent_hir_node(expr.hir_id); let return_type = cx.typeck_results().expr_ty(expr); - if let Node::Local(l) = parent_hir_node { + if let Node::LetStmt(l) = parent_hir_node { array_span_lint( cx, l.span, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index a526ba97af6..3b1b99caebe 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -99,7 +99,7 @@ use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::{ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, - ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, + ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, }; use rustc_lexer::{tokenize, TokenKind}; @@ -184,7 +184,7 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> { if let Node::Pat(pat) = cx.tcx.hir_node(hir_id) && matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..)) - && let Node::Local(local) = cx.tcx.parent_hir_node(hir_id) + && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id) { return local.init; } @@ -1079,7 +1079,7 @@ pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind { }, _ => break, }, - Node::Local(l) => match pat_capture_kind(cx, l.pat) { + Node::LetStmt(l) => match pat_capture_kind(cx, l.pat) { CaptureKind::Value => break, capture @ CaptureKind::Ref(_) => return capture, }, @@ -1357,7 +1357,7 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>( ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e), _ => (), }, - Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (), + Node::Stmt(_) | Node::Block(_) | Node::LetStmt(_) | Node::Arm(_) => (), _ => break, } } @@ -1462,7 +1462,7 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let mut child_id = expr.hir_id; for (parent_id, node) in tcx.hir().parent_iter(child_id) { - if let Node::Local(Local { + if let Node::LetStmt(LetStmt { init: Some(init), els: Some(els), .. @@ -1482,7 +1482,7 @@ pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { let mut child_id = expr.hir_id; for (parent_id, node) in tcx.hir().parent_iter(child_id) { - if let Node::Local(Local { els: Some(els), .. }) = node + if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node && els.hir_id == child_id { return true; @@ -2158,7 +2158,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { Node::Stmt(Stmt { kind: StmtKind::Expr(_) | StmtKind::Semi(_) - | StmtKind::Let(Local { + | StmtKind::Let(LetStmt { pat: Pat { kind: PatKind::Wild, .. @@ -2639,7 +2639,7 @@ pub struct ExprUseCtxt<'tcx> { /// The node which consumes a value. pub enum ExprUseNode<'tcx> { /// Assignment to, or initializer for, a local - Local(&'tcx Local<'tcx>), + LetStmt(&'tcx LetStmt<'tcx>), /// Initializer for a const or static item. ConstStatic(OwnerId), /// Implicit or explicit return from a function. @@ -2671,7 +2671,7 @@ impl<'tcx> ExprUseNode<'tcx> { /// Gets the needed type as it's defined without any type inference. pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> { match *self { - Self::Local(Local { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), + Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)), Self::ConstStatic(id) => Some(DefinedTy::Mir( cx.param_env .and(Binder::dummy(cx.tcx.type_of(id).instantiate_identity())), @@ -2731,7 +2731,7 @@ impl<'tcx> ExprUseNode<'tcx> { let sig = cx.tcx.fn_sig(id).skip_binder(); Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i)))) }, - Self::Local(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None, + Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None, } } } @@ -2770,7 +2770,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Optio .continue_value() .map(|(use_node, child_id)| { let node = match use_node { - Node::Local(l) => ExprUseNode::Local(l), + Node::LetStmt(l) => ExprUseNode::LetStmt(l), Node::ExprField(field) => ExprUseNode::Field(field), Node::Item(&Item { @@ -3158,7 +3158,7 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option< } } - fn visit_local(&mut self, l: &'tcx Local<'_>) { + fn visit_local(&mut self, l: &'tcx LetStmt<'_>) { if let Some(e) = l.init { self.visit_expr(e); } diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index dadb0d662ce..cabebf89bec 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -174,7 +174,7 @@ fn check_rvalue<'tcx>( )) } }, - Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbCheck(_), _) + Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf | NullOp::OffsetOf(_) | NullOp::UbChecks, _) | Rvalue::ShallowInitBox(_, _) => Ok(()), Rvalue::UnaryOp(_, operand) => { let ty = operand.ty(body, tcx); diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 7913926928f..762830ffd78 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -242,7 +242,7 @@ fn path_segment_certainty( Node::Param(..) => Certainty::Certain(None), // A local's type is certain if its type annotation is certain or it has an initializer whose // type is certain. - Node::Local(local) => { + Node::LetStmt(local) => { let lhs = local.ty.map_or(Certainty::Uncertain, |ty| type_certainty(cx, ty)); let rhs = local .init diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index c70005c2c58..1097fc6db72 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -206,7 +206,7 @@ jobs: run: | PR=$(gh pr create -B master --title 'Automatic Rustup' --body '') ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ - --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + --stream miri --subject "Miri Build Failure ($(date -u +%Y-%m))" \ --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.lock b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.lock index 7bf23225ea5..2a5f8f32254 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.lock +++ b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.lock @@ -3,5 +3,5 @@ version = 3 [[package]] -name = "invalidate" +name = "range-iteration" version = "0.1.0" diff --git a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.toml b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.toml index 14cf0882f0b..3f0146f6259 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/Cargo.toml +++ b/src/tools/miri/bench-cargo-miri/range-iteration/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "invalidate" +name = "range-iteration" version = "0.1.0" edition = "2021" diff --git a/src/tools/miri/bench-cargo-miri/invalidate/src/main.rs b/src/tools/miri/bench-cargo-miri/range-iteration/src/main.rs index fa8deb851c3..1ef35ee6b3c 100644 --- a/src/tools/miri/bench-cargo-miri/invalidate/src/main.rs +++ b/src/tools/miri/bench-cargo-miri/range-iteration/src/main.rs @@ -1,4 +1,5 @@ +//! This generates a lot of work for the AllocId part of the GC. fn main() { // The end of the range is just chosen to make the benchmark run for a few seconds. - for _ in 0..200_000 {} + for _ in 0..50_000 {} } diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e32968d8178..9b89f016a77 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -ee03c286cfdca26fa5b2a4ee40957625d2c826ff +c3b05c6e5b5b59613350b8c2875b0add67ed74df diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 6955e649b4d..c12382527e7 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -179,6 +179,26 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls { }); } } + + fn after_analysis<'tcx>( + &mut self, + _: &rustc_interface::interface::Compiler, + queries: &'tcx rustc_interface::Queries<'tcx>, + ) -> Compilation { + queries.global_ctxt().unwrap().enter(|tcx| { + if self.target_crate { + // cargo-miri has patched the compiler flags to make these into check-only builds, + // but we are still emulating regular rustc builds, which would perform post-mono + // const-eval during collection. So let's also do that here, even if we might be + // running with `--emit=metadata`. In particular this is needed to make + // `compile_fail` doc tests trigger post-mono errors. + // In general `collect_and_partition_mono_items` is not safe to call in check-only + // builds, but we are setting `-Zalways-encode-mir` which avoids those issues. + let _ = tcx.collect_and_partition_mono_items(()); + } + }); + Compilation::Continue + } } fn show_error(msg: &impl std::fmt::Display) -> ! { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index b26bbd16902..96ff298402d 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -18,6 +18,7 @@ use crate::borrow_tracker::{ stacked_borrows::diagnostics::{AllocHistory, DiagnosticCx, DiagnosticCxBuilder}, GlobalStateInner, ProtectorKind, }; +use crate::concurrency::data_race::{NaReadType, NaWriteType}; use crate::*; use diagnostics::{RetagCause, RetagInfo}; @@ -751,7 +752,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Write); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_mut() { - data_race.write(alloc_id, range, machine)?; + data_race.write( + alloc_id, + range, + NaWriteType::Retag, + Some(place.layout.ty), + machine, + )?; } } } @@ -794,7 +801,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' assert_eq!(access, AccessKind::Read); // Make sure the data race model also knows about this. if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } Ok(()) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 56ca1240f60..a3d49756e4c 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -9,8 +9,11 @@ use rustc_middle::{ use rustc_span::def_id::DefId; use rustc_target::abi::{Abi, Size}; -use crate::borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}; use crate::*; +use crate::{ + borrow_tracker::{GlobalState, GlobalStateInner, ProtectorKind}, + concurrency::data_race::NaReadType, +}; pub mod diagnostics; mod perms; @@ -312,7 +315,13 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' // Also inform the data race model (but only if any bytes are actually affected). if range.size.bytes() > 0 { if let Some(data_race) = alloc_extra.data_race.as_ref() { - data_race.read(alloc_id, range, &this.machine)?; + data_race.read( + alloc_id, + range, + NaReadType::Retag, + Some(place.layout.ty), + &this.machine, + )?; } } diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs index 127d97bd5af..d51160b2831 100644 --- a/src/tools/miri/src/concurrency/data_race.rs +++ b/src/tools/miri/src/concurrency/data_race.rs @@ -49,7 +49,7 @@ use std::{ use rustc_ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir; +use rustc_middle::{mir, ty::Ty}; use rustc_span::Span; use rustc_target::abi::{Align, HasDataLayout, Size}; @@ -200,18 +200,38 @@ enum AtomicAccessType { Rmw, } -/// Type of write operation: allocating memory -/// non-atomic writes and deallocating memory -/// are all treated as writes for the purpose -/// of the data-race detector. +/// Type of a non-atomic read operation. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -enum NaWriteType { +pub enum NaReadType { + /// Standard unsynchronized write. + Read, + + // An implicit read generated by a retag. + Retag, +} + +impl NaReadType { + fn description(self) -> &'static str { + match self { + NaReadType::Read => "non-atomic read", + NaReadType::Retag => "retag read", + } + } +} + +/// Type of a non-atomic write operation: allocating memory, non-atomic writes, and +/// deallocating memory are all treated as writes for the purpose of the data-race detector. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum NaWriteType { /// Allocate memory. Allocate, /// Standard unsynchronized write. Write, + // An implicit write generated by a retag. + Retag, + /// Deallocate memory. /// Note that when memory is deallocated first, later non-atomic accesses /// will be reported as use-after-free, not as data races. @@ -224,6 +244,7 @@ impl NaWriteType { match self { NaWriteType::Allocate => "creating a new allocation", NaWriteType::Write => "non-atomic write", + NaWriteType::Retag => "retag write", NaWriteType::Deallocate => "deallocation", } } @@ -231,7 +252,7 @@ impl NaWriteType { #[derive(Copy, Clone, PartialEq, Eq, Debug)] enum AccessType { - NaRead, + NaRead(NaReadType), NaWrite(NaWriteType), AtomicLoad, AtomicStore, @@ -239,29 +260,48 @@ enum AccessType { } impl AccessType { - fn description(self) -> &'static str { - match self { - AccessType::NaRead => "non-atomic read", + fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String { + let mut msg = String::new(); + + if let Some(size) = size { + msg.push_str(&format!("{}-byte {}", size.bytes(), msg)) + } + + msg.push_str(match self { + AccessType::NaRead(w) => w.description(), AccessType::NaWrite(w) => w.description(), AccessType::AtomicLoad => "atomic load", AccessType::AtomicStore => "atomic store", AccessType::AtomicRmw => "atomic read-modify-write", + }); + + if let Some(ty) = ty { + msg.push_str(&format!(" of type `{}`", ty)); } + + msg } fn is_atomic(self) -> bool { match self { AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true, - AccessType::NaRead | AccessType::NaWrite(_) => false, + AccessType::NaRead(_) | AccessType::NaWrite(_) => false, } } fn is_read(self) -> bool { match self { - AccessType::AtomicLoad | AccessType::NaRead => true, + AccessType::AtomicLoad | AccessType::NaRead(_) => true, AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false, } } + + fn is_retag(self) -> bool { + matches!( + self, + AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag) + ) + } } /// Memory Cell vector clock metadata @@ -502,12 +542,14 @@ impl MemoryCellClocks { &mut self, thread_clocks: &mut ThreadClockSet, index: VectorIdx, + read_type: NaReadType, current_span: Span, ) -> Result<(), DataRace> { trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks); if !current_span.is_dummy() { thread_clocks.clock[index].span = current_span; } + thread_clocks.clock[index].set_read_type(read_type); if self.write_was_before(&thread_clocks.clock) { let race_free = if let Some(atomic) = self.atomic() { // We must be ordered-after all atomic accesses, reads and writes. @@ -875,7 +917,8 @@ impl VClockAlloc { /// This finds the two racing threads and the type /// of data-race that occurred. This will also /// return info about the memory location the data-race - /// occurred in. + /// occurred in. The `ty` parameter is used for diagnostics, letting + /// the user know which type was involved in the access. #[cold] #[inline(never)] fn report_data_race<'tcx>( @@ -885,6 +928,7 @@ impl VClockAlloc { access: AccessType, access_size: Size, ptr_dbg: Pointer<AllocId>, + ty: Option<Ty<'_>>, ) -> InterpResult<'tcx> { let (current_index, current_clocks) = global.current_thread_state(thread_mgr); let mut other_size = None; // if `Some`, this was a size-mismatch race @@ -908,7 +952,7 @@ impl VClockAlloc { write_clock = mem_clocks.write(); (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock) } else if let Some(idx) = Self::find_gt_index(&mem_clocks.read, ¤t_clocks.clock) { - (AccessType::NaRead, idx, &mem_clocks.read) + (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read) // Finally, mixed-size races. } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != access_size { // This is only a race if we are not synchronized with all atomic accesses, so find @@ -950,37 +994,33 @@ impl VClockAlloc { Err(err_machine_stop!(TerminationInfo::DataRace { involves_non_atomic, extra, + retag_explain: access.is_retag() || other_access.is_retag(), ptr: ptr_dbg, op1: RacingOp { - action: if let Some(other_size) = other_size { - format!("{}-byte {}", other_size.bytes(), other_access.description()) - } else { - other_access.description().to_owned() - }, + action: other_access.description(None, other_size), thread_info: other_thread_info, span: other_clock.as_slice()[other_thread.index()].span_data(), }, op2: RacingOp { - action: if other_size.is_some() { - format!("{}-byte {}", access_size.bytes(), access.description()) - } else { - access.description().to_owned() - }, + action: access.description(ty, other_size.map(|_| access_size)), thread_info: current_thread_info, span: current_clocks.clock.as_slice()[current_index.index()].span_data(), }, }))? } - /// Detect data-races for an unsynchronized read operation, will not perform + /// Detect data-races for an unsynchronized read operation. It will not perform /// data-race detection if `race_detecting()` is false, either due to no threads /// being created or if it is temporarily disabled during a racy read or write /// operation for which data-race detection is handled separately, for example - /// atomic read operations. + /// atomic read operations. The `ty` parameter is used for diagnostics, letting + /// the user know which type was read. pub fn read<'tcx>( &self, alloc_id: AllocId, access_range: AllocRange, + read_type: NaReadType, + ty: Option<Ty<'_>>, machine: &MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -992,7 +1032,7 @@ impl VClockAlloc { alloc_ranges.iter_mut(access_range.start, access_range.size) { if let Err(DataRace) = - mem_clocks.read_race_detect(&mut thread_clocks, index, current_span) + mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span) { drop(thread_clocks); // Report data-race. @@ -1000,9 +1040,10 @@ impl VClockAlloc { global, &machine.threads, mem_clocks, - AccessType::NaRead, + AccessType::NaRead(read_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1012,12 +1053,17 @@ impl VClockAlloc { } } - // Shared code for detecting data-races on unique access to a section of memory - fn unique_access<'tcx>( + /// Detect data-races for an unsynchronized write operation. It will not perform + /// data-race detection if `race_detecting()` is false, either due to no threads + /// being created or if it is temporarily disabled during a racy read or write + /// operation. The `ty` parameter is used for diagnostics, letting + /// the user know which type was written. + pub fn write<'tcx>( &mut self, alloc_id: AllocId, access_range: AllocRange, write_type: NaWriteType, + ty: Option<Ty<'_>>, machine: &mut MiriMachine<'_, '_>, ) -> InterpResult<'tcx> { let current_span = machine.current_span(); @@ -1042,6 +1088,7 @@ impl VClockAlloc { AccessType::NaWrite(write_type), access_range.size, Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)), + ty, ); } } @@ -1050,37 +1097,6 @@ impl VClockAlloc { Ok(()) } } - - /// Detect data-races for an unsynchronized write operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn write<'tcx>( - &mut self, - alloc_id: AllocId, - range: AllocRange, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access(alloc_id, range, NaWriteType::Write, machine) - } - - /// Detect data-races for an unsynchronized deallocate operation, will not perform - /// data-race threads if `race_detecting()` is false, either due to no threads - /// being created or if it is temporarily disabled during a racy read or write - /// operation - pub fn deallocate<'tcx>( - &mut self, - alloc_id: AllocId, - size: Size, - machine: &mut MiriMachine<'_, '_>, - ) -> InterpResult<'tcx> { - self.unique_access( - alloc_id, - alloc_range(Size::ZERO, size), - NaWriteType::Deallocate, - machine, - ) - } } impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriInterpCx<'mir, 'tcx> {} @@ -1279,7 +1295,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_ref().unwrap(); trace!( "Atomic op({}) with ordering {:?} on {:?} (size={})", - access.description(), + access.description(None, None), &atomic, place.ptr(), size.bytes() @@ -1307,6 +1323,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriInterpCxExt<'mir, 'tcx> { alloc_id, Size::from_bytes(mem_clocks_range.start), ), + None, ) .map(|_| true); } diff --git a/src/tools/miri/src/concurrency/vector_clock.rs b/src/tools/miri/src/concurrency/vector_clock.rs index fa93c9e00b1..fe719943dcb 100644 --- a/src/tools/miri/src/concurrency/vector_clock.rs +++ b/src/tools/miri/src/concurrency/vector_clock.rs @@ -4,9 +4,11 @@ use smallvec::SmallVec; use std::{ cmp::Ordering, fmt::Debug, - ops::{Index, IndexMut}, + ops::{Index, IndexMut, Shr}, }; +use super::data_race::NaReadType; + /// A vector clock index, this is associated with a thread id /// but in some cases one vector index may be shared with /// multiple thread ids if it's safe to do so. @@ -50,13 +52,51 @@ const SMALL_VECTOR: usize = 4; /// so that diagnostics can report what code was responsible for an operation. #[derive(Clone, Copy, Debug)] pub struct VTimestamp { - time: u32, + /// The lowest bit indicates read type, the rest is the time. + /// `1` indicates a retag read, `0` a regular read. + time_and_read_type: u32, pub span: Span, } impl VTimestamp { - pub const ZERO: VTimestamp = VTimestamp { time: 0, span: DUMMY_SP }; + pub const ZERO: VTimestamp = VTimestamp::new(0, NaReadType::Read, DUMMY_SP); + + #[inline] + const fn encode_time_and_read_type(time: u32, read_type: NaReadType) -> u32 { + let read_type_bit = match read_type { + NaReadType::Read => 0, + NaReadType::Retag => 1, + }; + // Put the `read_type` in the lowest bit and `time` in the rest + read_type_bit | time.checked_mul(2).expect("Vector clock overflow") + } + + #[inline] + const fn new(time: u32, read_type: NaReadType, span: Span) -> Self { + Self { time_and_read_type: Self::encode_time_and_read_type(time, read_type), span } + } + + #[inline] + fn time(&self) -> u32 { + self.time_and_read_type.shr(1) + } + #[inline] + fn set_time(&mut self, time: u32) { + self.time_and_read_type = Self::encode_time_and_read_type(time, self.read_type()); + } + + #[inline] + pub fn read_type(&self) -> NaReadType { + if self.time_and_read_type & 1 == 0 { NaReadType::Read } else { NaReadType::Retag } + } + + #[inline] + pub fn set_read_type(&mut self, read_type: NaReadType) { + self.time_and_read_type = Self::encode_time_and_read_type(self.time(), read_type); + } + + #[inline] pub fn span_data(&self) -> SpanData { self.span.data() } @@ -64,7 +104,7 @@ impl VTimestamp { impl PartialEq for VTimestamp { fn eq(&self, other: &Self) -> bool { - self.time == other.time + self.time() == other.time() } } @@ -78,7 +118,7 @@ impl PartialOrd for VTimestamp { impl Ord for VTimestamp { fn cmp(&self, other: &Self) -> Ordering { - self.time.cmp(&other.time) + self.time().cmp(&other.time()) } } @@ -130,7 +170,7 @@ impl VClock { let idx = idx.index(); let mut_slice = self.get_mut_with_min_len(idx + 1); let idx_ref = &mut mut_slice[idx]; - idx_ref.time = idx_ref.time.checked_add(1).expect("Vector clock overflow"); + idx_ref.set_time(idx_ref.time().checked_add(1).expect("Vector clock overflow")); if !current_span.is_dummy() { idx_ref.span = current_span; } @@ -379,8 +419,8 @@ impl IndexMut<VectorIdx> for VClock { /// test suite #[cfg(test)] mod tests { - use super::{VClock, VTimestamp, VectorIdx}; + use crate::concurrency::data_race::NaReadType; use rustc_span::DUMMY_SP; use std::cmp::Ordering; @@ -448,7 +488,13 @@ mod tests { while let Some(0) = slice.last() { slice = &slice[..slice.len() - 1] } - VClock(slice.iter().copied().map(|time| VTimestamp { time, span: DUMMY_SP }).collect()) + VClock( + slice + .iter() + .copied() + .map(|time| VTimestamp::new(time, NaReadType::Read, DUMMY_SP)) + .collect(), + ) } fn assert_order(l: &[u32], r: &[u32], o: Option<Ordering>) { diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 03428b081c5..99d37065bac 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -46,6 +46,7 @@ pub enum TerminationInfo { op1: RacingOp, op2: RacingOp, extra: Option<&'static str>, + retag_explain: bool, }, } @@ -263,12 +264,17 @@ pub fn report_error<'tcx, 'mir>( vec![(Some(*span), format!("the `{link_name}` symbol is defined here"))], Int2PtrWithStrictProvenance => vec![(None, format!("use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead"))], - DataRace { op1, extra, .. } => { + DataRace { op1, extra, retag_explain, .. } => { let mut helps = vec![(Some(op1.span), format!("and (1) occurred earlier here"))]; if let Some(extra) = extra { helps.push((None, format!("{extra}"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model"))); } + if *retag_explain { + helps.push((None, "retags occur on all (re)borrows and as well as when references are copied or moved".to_owned())); + helps.push((None, "retags permit optimizations that insert speculative reads or writes".to_owned())); + helps.push((None, "therefore from the perspective of data races, a retag has the same implications as a read or write".to_owned())); + } helps.push((None, format!("this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior"))); helps.push((None, format!("see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information"))); helps diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 416d0cda8f1..7821aa9efd4 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -1,5 +1,6 @@ #![feature(rustc_private)] #![feature(cell_update)] +#![feature(const_option)] #![feature(float_gamma)] #![feature(generic_nonzero)] #![feature(map_try_insert)] diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 29315c4933c..2137de6a29b 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -35,6 +35,9 @@ use crate::{ *, }; +use self::concurrency::data_race::NaReadType; +use self::concurrency::data_race::NaWriteType; + /// First real-time signal. /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35 /// as typical values. @@ -372,10 +375,8 @@ pub struct PrimitiveLayouts<'tcx> { impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>> { let tcx = layout_cx.tcx; - let mut_raw_ptr = - Ty::new_mut_ptr(tcx, tcx.types.unit); - let const_raw_ptr = - Ty::new_imm_ptr(tcx, tcx.types.unit); + let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit); + let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit); Ok(Self { unit: layout_cx.layout_of(Ty::new_unit(tcx))?, i8: layout_cx.layout_of(tcx.types.i8)?, @@ -1240,7 +1241,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read)); } if let Some(data_race) = &alloc_extra.data_race { - data_race.read(alloc_id, range, machine)?; + data_race.read(alloc_id, range, NaReadType::Read, None, machine)?; } if let Some(borrow_tracker) = &alloc_extra.borrow_tracker { borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?; @@ -1264,7 +1265,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.write(alloc_id, range, machine)?; + data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?; @@ -1288,7 +1289,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> { machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id)); } if let Some(data_race) = &mut alloc_extra.data_race { - data_race.deallocate(alloc_id, size, machine)?; + data_race.write( + alloc_id, + alloc_range(Size::ZERO, size), + NaWriteType::Deallocate, + None, + machine, + )?; } if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker { borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?; diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index c97a052f517..6973c0e9c35 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -33,6 +33,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { | "round" | "trunc" | "fsqrt" + | "fsin" + | "fcos" + | "fexp" + | "fexp2" + | "flog" + | "flog2" + | "flog10" | "ctlz" | "cttz" | "bswap" @@ -45,17 +52,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { assert_eq!(dest_len, op_len); #[derive(Copy, Clone)] - enum Op { + enum Op<'a> { MirOp(mir::UnOp), Abs, - Sqrt, Round(rustc_apfloat::Round), Numeric(Symbol), + HostOp(&'a str), } let which = match intrinsic_name { "neg" => Op::MirOp(mir::UnOp::Neg), "fabs" => Op::Abs, - "fsqrt" => Op::Sqrt, "ceil" => Op::Round(rustc_apfloat::Round::TowardPositive), "floor" => Op::Round(rustc_apfloat::Round::TowardNegative), "round" => Op::Round(rustc_apfloat::Round::NearestTiesToAway), @@ -64,7 +70,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "cttz" => Op::Numeric(sym::cttz), "bswap" => Op::Numeric(sym::bswap), "bitreverse" => Op::Numeric(sym::bitreverse), - _ => unreachable!(), + _ => Op::HostOp(intrinsic_name), }; for i in 0..dest_len { @@ -89,7 +95,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F128 => unimplemented!("f16_f128"), } } - Op::Sqrt => { + Op::HostOp(host_op) => { let ty::Float(float_ty) = op.layout.ty.kind() else { span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name) }; @@ -98,13 +104,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { FloatTy::F16 => unimplemented!("f16_f128"), FloatTy::F32 => { let f = op.to_scalar().to_f32()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } FloatTy::F64 => { let f = op.to_scalar().to_f64()?; - let res = f.to_host().sqrt().to_soft(); + let f_host = f.to_host(); + let res = match host_op { + "fsqrt" => f_host.sqrt(), + "fsin" => f_host.sin(), + "fcos" => f_host.cos(), + "fexp" => f_host.exp(), + "fexp2" => f_host.exp2(), + "flog" => f_host.ln(), + "flog2" => f_host.log2(), + "flog10" => f_host.log10(), + _ => bug!(), + }; + let res = res.to_soft(); let res = this.adjust_nan(res, &[f]); Scalar::from(res) } @@ -427,7 +457,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let bitmask_len = u32::try_from(bitmask_len).unwrap(); // To read the mask, we transmute it to an integer. - // That does the right thing wrt endianess. + // That does the right thing wrt endianness. let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap(); let mask = mask.transmute(mask_ty, this)?; let mask: u64 = this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap(); @@ -479,7 +509,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } // We have to change the type of the place to be able to write `res` into it. This - // transmutes the integer to an array, which does the right thing wrt endianess. + // transmutes the integer to an array, which does the right thing wrt endianness. let dest = dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?; this.write_int(res, &dest)?; diff --git a/src/tools/miri/src/shims/unix/linux/fd/event.rs b/src/tools/miri/src/shims/unix/linux/fd/event.rs index 1f17ffb88c8..49408fda3ae 100644 --- a/src/tools/miri/src/shims/unix/linux/fd/event.rs +++ b/src/tools/miri/src/shims/unix/linux/fd/event.rs @@ -38,7 +38,7 @@ impl FileDescriptor for Event { } /// A write call adds the 8-byte integer value supplied in - /// its buffer (in native endianess) to the counter. The maximum value that may be + /// its buffer (in native endianness) to the counter. The maximum value that may be /// stored in the counter is the largest unsigned 64-bit value /// minus 1 (i.e., 0xfffffffffffffffe). If the addition would /// cause the counter's value to exceed the maximum, then the @@ -57,7 +57,7 @@ impl FileDescriptor for Event { ) -> InterpResult<'tcx, io::Result<usize>> { let v1 = self.val.get(); let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size - // Convert from target endianess to host endianess. + // Convert from target endianness to host endianness. let num = match tcx.sess.target.endian { Endian::Little => u64::from_le_bytes(bytes), Endian::Big => u64::from_be_bytes(bytes), diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 7cd397625dc..7b7921219e6 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -88,6 +88,19 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.write_immediate(*sub, &this.project_field(dest, 1)?)?; } + // Used to implement the `_mm_pause` function. + // The intrinsic is used to hint the processor that the code is in a spin-loop. + // It is compiled down to a `pause` instruction. When SSE2 is not available, + // the instruction behaves like a no-op, so it is always safe to call the + // intrinsic. + "sse2.pause" => { + let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; + // Only exhibit the spin-loop hint behavior when SSE2 is enabled. + if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) { + this.yield_active_thread(); + } + } + name if name.starts_with("sse.") => { return sse::EvalContextExt::emulate_x86_sse_intrinsic( this, link_name, abi, args, dest, diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 18ff5d809e3..eb2cc9d37c8 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -580,12 +580,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?; } } - // Used to implement the `_mm_pause` function. - // The intrinsic is used to hint the processor that the code is in a spin-loop. - "pause" => { - let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?; - this.yield_active_thread(); - } _ => return Ok(EmulateForeignItemResult::NotSupported), } Ok(EmulateForeignItemResult::NeedsJumping) diff --git a/src/tools/miri/test-cargo-miri/src/lib.rs b/src/tools/miri/test-cargo-miri/src/lib.rs index 66c8aa2eac5..e6b8c4ef65b 100644 --- a/src/tools/miri/test-cargo-miri/src/lib.rs +++ b/src/tools/miri/test-cargo-miri/src/lib.rs @@ -1,13 +1,31 @@ /// Doc-test test +/// /// ```rust /// assert!(cargo_miri_test::make_true()); /// ``` +/// +/// `no_run` test: +/// /// ```rust,no_run /// assert!(!cargo_miri_test::make_true()); /// ``` +/// +/// `compile_fail` test: +/// /// ```rust,compile_fail /// assert!(cargo_miri_test::make_true() == 5); /// ``` +/// +/// Post-monomorphization error in `compile_fail` test: +/// +/// ```rust,compile_fail +/// struct Fail<T>(T); +/// impl<T> Fail<T> { +/// const C: () = panic!(); +/// } +/// +/// let _val = Fail::<i32>::C; +/// ``` #[no_mangle] pub fn make_true() -> bool { issue_1567::use_the_dependency(); diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref index 9a17f3d61b6..922d2120bed 100644 --- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref @@ -10,7 +10,7 @@ running 6 tests test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out -running 4 tests -.... -test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +running 5 tests +..... +test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref index c618956656a..5c819dd5323 100644 --- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref +++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref @@ -13,5 +13,5 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out running 0 tests -test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 4 filtered out; finished in $TIME +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs index eb1fe56df07..3edaf10f3dc 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.rs @@ -17,7 +17,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: /Data race detected between \(1\) non-atomic (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ + *p = 5; //~ ERROR: /Data race detected between \(1\) retag (read|write) on thread `unnamed-[0-9]+` and \(2\) non-atomic write on thread `unnamed-[0-9]+`/ } } diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr index c5b65e6f747..6f4b52fb887 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.stack.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag write on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr index 62f139f6f08..fa0012f9b26 100644 --- a/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr +++ b/src/tools/miri/tests/fail/both_borrows/retag_data_race_write.tree.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_write.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_write.rs:LL:CC | LL | let _r = &mut *p; | ^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs new file mode 100644 index 00000000000..89fdd2a01eb --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.rs @@ -0,0 +1,19 @@ +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +use std::intrinsics::typed_swap; +use std::ptr::addr_of_mut; + +fn invalid_array() { + let mut a = [1_u8; 100]; + let mut b = [2_u8; 100]; + unsafe { + let a = addr_of_mut!(a).cast::<[bool; 100]>(); + let b = addr_of_mut!(b).cast::<[bool; 100]>(); + typed_swap(a, b); //~ERROR: constructing invalid value + } +} + +fn main() { + invalid_array(); +} diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr new file mode 100644 index 00000000000..15f01c1c095 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-array.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: constructing invalid value at [0]: encountered 0x02, but expected a boolean + --> $DIR/typed-swap-invalid-array.rs:LL:CC + | +LL | typed_swap(a, b); + | ^^^^^^^^^^^^^^^^ constructing invalid value at [0]: encountered 0x02, but expected a boolean + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `invalid_array` at $DIR/typed-swap-invalid-array.rs:LL:CC +note: inside `main` + --> $DIR/typed-swap-invalid-array.rs:LL:CC + | +LL | invalid_array(); + | ^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs new file mode 100644 index 00000000000..9d014a523f8 --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.rs @@ -0,0 +1,19 @@ +#![feature(core_intrinsics)] +#![feature(rustc_attrs)] + +use std::intrinsics::typed_swap; +use std::ptr::addr_of_mut; + +fn invalid_scalar() { + let mut a = 1_u8; + let mut b = 2_u8; + unsafe { + let a = addr_of_mut!(a).cast::<bool>(); + let b = addr_of_mut!(b).cast::<bool>(); + typed_swap(a, b); //~ERROR: constructing invalid value + } +} + +fn main() { + invalid_scalar(); +} diff --git a/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr new file mode 100644 index 00000000000..262ca202f9f --- /dev/null +++ b/src/tools/miri/tests/fail/intrinsics/typed-swap-invalid-scalar.stderr @@ -0,0 +1,20 @@ +error: Undefined Behavior: constructing invalid value: encountered 0x02, but expected a boolean + --> $DIR/typed-swap-invalid-scalar.rs:LL:CC + | +LL | typed_swap(a, b); + | ^^^^^^^^^^^^^^^^ constructing invalid value: encountered 0x02, but expected a boolean + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `invalid_scalar` at $DIR/typed-swap-invalid-scalar.rs:LL:CC +note: inside `main` + --> $DIR/typed-swap-invalid-scalar.rs:LL:CC + | +LL | invalid_scalar(); + | ^^^^^^^^^^^^^^^^ + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs index 5db89c89b77..3de517055ec 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.rs @@ -13,7 +13,7 @@ fn main() { let ptr = ptr; // We do a protected mutable retag (but no write!) in this thread. fn retag(_x: &mut i32) {} - retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-1` + retag(unsafe { &mut *ptr.0 }); //~ERROR: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-1` }); // We do a read in the main thread. diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr index 2ce757013d5..47ae4b5d46d 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_protected_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | retag(unsafe { &mut *ptr.0 }); - | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^^^^^^ Data race detected between (1) non-atomic read on thread `main` and (2) retag write of type `i32` on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_protected_read.rs:LL:CC | LL | unsafe { ptr.0.read() }; | ^^^^^^^^^^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs index 01a2e9ac474..25c92ddf6ca 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.rs @@ -15,7 +15,7 @@ fn thread_1(p: SendPtr) { fn thread_2(p: SendPtr) { let p = p.0; unsafe { - *p = 5; //~ ERROR: Data race detected between (1) non-atomic read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` + *p = 5; //~ ERROR: Data race detected between (1) retag read on thread `unnamed-1` and (2) non-atomic write on thread `unnamed-2` } } diff --git a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr index d3c8d14e2a1..9fe9fbeda44 100644 --- a/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr +++ b/src/tools/miri/tests/fail/stacked_borrows/retag_data_race_read.stderr @@ -1,14 +1,17 @@ -error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here +error: Undefined Behavior: Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here --> $DIR/retag_data_race_read.rs:LL:CC | LL | *p = 5; - | ^^^^^^ Data race detected between (1) non-atomic read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here + | ^^^^^^ Data race detected between (1) retag read on thread `unnamed-ID` and (2) non-atomic write on thread `unnamed-ID` at ALLOC. (2) just happened here | help: and (1) occurred earlier here --> $DIR/retag_data_race_read.rs:LL:CC | LL | let _r = &*p; | ^^^ + = help: retags occur on all (re)borrows and as well as when references are copied or moved + = help: retags permit optimizations that insert speculative reads or writes + = help: therefore from the perspective of data races, a retag has the same implications as a read or write = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE (of the first span) on thread `unnamed-ID`: diff --git a/src/tools/miri/tests/pass/intptrcast.rs b/src/tools/miri/tests/pass/intptrcast.rs index 370b09f512c..4e9fa12c181 100644 --- a/src/tools/miri/tests/pass/intptrcast.rs +++ b/src/tools/miri/tests/pass/intptrcast.rs @@ -149,6 +149,31 @@ fn functions() { } } +/// Example that should be UB but due to wildcard pointers being too permissive +/// we don't notice. +fn should_be_ub() { + let alloc1 = 1u8; + let alloc2 = 2u8; + // Expose both allocations + let addr1: usize = &alloc1 as *const u8 as usize; + let addr2: usize = &alloc2 as *const u8 as usize; + + // Cast addr1 back to a pointer. In Miri, this gives it Wildcard provenance. + let wildcard = addr1 as *const u8; + unsafe { + // Read through the wildcard + assert_eq!(*wildcard, 1); + // Offset the pointer to another allocation. + // Note that we are doing this arithmetic that does not require we stay within bounds of the allocation. + let wildcard = wildcard.wrapping_offset(addr2 as isize - addr1 as isize); + // This should report UB: + assert_eq!(*wildcard, 2); + // ... but it doesn't. A pointer's provenance specifies a single allocation that it is allowed to read from. + // And wrapping_offset only modifies the address, not the provenance. + // So which allocation is wildcard allowed to access? It cannot be both. + } +} + fn main() { cast(); cast_dangling(); @@ -162,4 +187,5 @@ fn main() { ptr_eq_integer(); zst_deref_of_dangling(); functions(); + should_be_ub(); } diff --git a/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs b/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs new file mode 100644 index 00000000000..c8b92fd5458 --- /dev/null +++ b/src/tools/miri/tests/pass/intrinsics-x86-pause-without-sse2.rs @@ -0,0 +1,25 @@ +// Ignore everything except x86 and x86_64 +// Any new targets that are added to CI should be ignored here. +// (We cannot use `cfg`-based tricks here since the `target-feature` flags below only work on x86.) +//@ignore-target-aarch64 +//@ignore-target-arm +//@ignore-target-avr +//@ignore-target-s390x +//@ignore-target-thumbv7em +//@ignore-target-wasm32 +//@compile-flags: -C target-feature=-sse2 + +#[cfg(target_arch = "x86")] +use std::arch::x86::*; +#[cfg(target_arch = "x86_64")] +use std::arch::x86_64::*; + +fn main() { + assert!(!is_x86_feature_detected!("sse2")); + + unsafe { + // This is a SSE2 intrinsic, but it behaves as a no-op when SSE2 + // is not available, so it is always safe to call. + _mm_pause(); + } +} diff --git a/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs index e636d6c8aaf..e0088b9eb24 100644 --- a/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs +++ b/src/tools/miri/tests/pass/intrinsics-x86-sse2.rs @@ -54,6 +54,11 @@ mod tests { } } + fn test_mm_pause() { + unsafe { _mm_pause() } + } + test_mm_pause(); + #[target_feature(enable = "sse2")] unsafe fn test_mm_avg_epu8() { let (a, b) = (_mm_set1_epi8(3), _mm_set1_epi8(9)); diff --git a/src/tools/miri/tests/pass/main_fn.rs b/src/tools/miri/tests/pass/main_fn.rs deleted file mode 100644 index 4cdd034f30e..00000000000 --- a/src/tools/miri/tests/pass/main_fn.rs +++ /dev/null @@ -1,5 +0,0 @@ -mod foo { - pub(crate) fn bar() {} -} - -use foo::bar as main; diff --git a/src/tools/miri/tests/pass/portable-simd.rs b/src/tools/miri/tests/pass/portable-simd.rs index 399913a757b..cdb441b450b 100644 --- a/src/tools/miri/tests/pass/portable-simd.rs +++ b/src/tools/miri/tests/pass/portable-simd.rs @@ -526,6 +526,23 @@ fn simd_intrinsics() { } } +fn simd_float_intrinsics() { + use intrinsics::*; + + // These are just smoke tests to ensure the intrinsics can be called. + unsafe { + let a = f32x4::splat(10.0); + simd_fsqrt(a); + simd_fsin(a); + simd_fcos(a); + simd_fexp(a); + simd_fexp2(a); + simd_flog(a); + simd_flog2(a); + simd_flog10(a); + } +} + fn simd_masked_loadstore() { // The buffer is deliberarely too short, so reading the last element would be UB. let buf = [3i32; 3]; @@ -559,5 +576,6 @@ fn main() { simd_gather_scatter(); simd_round(); simd_intrinsics(); + simd_float_intrinsics(); simd_masked_loadstore(); } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index e5e7b559c92..419b04231b5 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -9,8 +9,8 @@ pub fn out_dir() -> PathBuf { env::var_os("TMPDIR").unwrap().into() } -fn setup_common_build_cmd() -> Command { - let rustc = env::var("RUSTC").unwrap(); +fn setup_common_build_cmd(command: &str) -> Command { + let rustc = env::var(command).unwrap(); let mut cmd = Command::new(rustc); cmd.arg("--out-dir").arg(out_dir()).arg("-L").arg(out_dir()); cmd @@ -33,6 +33,10 @@ pub fn aux_build() -> AuxBuildInvocationBuilder { AuxBuildInvocationBuilder::new() } +pub fn rustdoc() -> Rustdoc { + Rustdoc::new() +} + #[derive(Debug)] pub struct RustcInvocationBuilder { cmd: Command, @@ -40,7 +44,7 @@ pub struct RustcInvocationBuilder { impl RustcInvocationBuilder { fn new() -> Self { - let cmd = setup_common_build_cmd(); + let cmd = setup_common_build_cmd("RUSTC"); Self { cmd } } @@ -74,7 +78,7 @@ pub struct AuxBuildInvocationBuilder { impl AuxBuildInvocationBuilder { fn new() -> Self { - let mut cmd = setup_common_build_cmd(); + let mut cmd = setup_common_build_cmd("RUSTC"); cmd.arg("--crate-type=lib"); Self { cmd } } @@ -97,6 +101,35 @@ impl AuxBuildInvocationBuilder { } } +#[derive(Debug)] +pub struct Rustdoc { + cmd: Command, +} + +impl Rustdoc { + fn new() -> Self { + let cmd = setup_common_build_cmd("RUSTDOC"); + Self { cmd } + } + + pub fn arg(&mut self, arg: &str) -> &mut Self { + self.cmd.arg(arg); + self + } + + #[track_caller] + pub fn run(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } +} + fn run_common(bin_name: &str) -> (Command, Output) { let target = env::var("TARGET").unwrap(); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index cb46e65999d..1a39d212386 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -121,6 +121,7 @@ fn default_dcx( fallback_bundle, fatal_dcx: DiagCtxt::new(emitter), fatal_note: None, + emit_fatal_diagnostic: false, }) } else { emitter @@ -209,7 +210,7 @@ impl ParseSess { rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - self.raw_psess.dcx.make_silent(fallback_bundle, None); + self.raw_psess.dcx.make_silent(fallback_bundle, None, false); } pub(crate) fn span_to_filename(&self, span: Span) -> FileName { diff --git a/tests/assembly/x86_64-typed-swap.rs b/tests/assembly/x86_64-typed-swap.rs new file mode 100644 index 00000000000..95e87519e6c --- /dev/null +++ b/tests/assembly/x86_64-typed-swap.rs @@ -0,0 +1,53 @@ +//@ revisions: WIN LIN +//@ [WIN] only-windows +//@ [LIN] only-linux +//@ only-x86_64 +//@ assembly-output: emit-asm +//@ compile-flags: --crate-type=lib -O + +use std::arch::x86_64::__m128; +use std::mem::swap; + +// CHECK-LABEL: swap_i32: +#[no_mangle] +pub fn swap_i32(x: &mut i32, y: &mut i32) { + // CHECK: movl (%[[ARG1:.+]]), %[[T1:.+]] + // CHECK: movl (%[[ARG2:.+]]), %[[T2:.+]] + // CHECK: movl %[[T2]], (%[[ARG1]]) + // CHECK: movl %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_pair: +#[no_mangle] +pub fn swap_pair(x: &mut (i32, u32), y: &mut (i32, u32)) { + // CHECK: movq (%[[ARG1]]), %[[T1:.+]] + // CHECK: movq (%[[ARG2]]), %[[T2:.+]] + // CHECK: movq %[[T2]], (%[[ARG1]]) + // CHECK: movq %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_str: +#[no_mangle] +pub fn swap_str<'a>(x: &mut &'a str, y: &mut &'a str) { + // CHECK: movups (%[[ARG1]]), %[[T1:xmm.]] + // CHECK: movups (%[[ARG2]]), %[[T2:xmm.]] + // CHECK: movups %[[T2]], (%[[ARG1]]) + // CHECK: movups %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} + +// CHECK-LABEL: swap_simd: +#[no_mangle] +pub fn swap_simd(x: &mut __m128, y: &mut __m128) { + // CHECK: movaps (%[[ARG1]]), %[[T1:xmm.]] + // CHECK: movaps (%[[ARG2]]), %[[T2:xmm.]] + // CHECK: movaps %[[T2]], (%[[ARG1]]) + // CHECK: movaps %[[T1]], (%[[ARG2]]) + // CHECK: retq + swap(x, y) +} diff --git a/tests/codegen/intrinsics/typed_swap.rs b/tests/codegen/intrinsics/typed_swap.rs new file mode 100644 index 00000000000..b55fb8ee36f --- /dev/null +++ b/tests/codegen/intrinsics/typed_swap.rs @@ -0,0 +1,78 @@ +//@ revisions: OPT0 OPT3 +//@ [OPT0] compile-flags: -Copt-level=0 +//@ [OPT3] compile-flags: -Copt-level=3 +//@ compile-flags: -C no-prepopulate-passes +//@ only-64bit (so I don't need to worry about usize) +// ignore-tidy-linelength (the memcpy calls get long) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +use std::intrinsics::typed_swap; + +// CHECK-LABEL: @swap_unit( +#[no_mangle] +pub unsafe fn swap_unit(x: &mut (), y: &mut ()) { + // CHECK: start + // CHECK-NEXT: ret void + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_i32( +#[no_mangle] +pub unsafe fn swap_i32(x: &mut i32, y: &mut i32) { + // CHECK-NOT: alloca + + // CHECK: %[[TEMP:.+]] = load i32, ptr %x, align 4 + // CHECK-SAME: !noundef + // OPT0: %[[TEMP2:.+]] = load i32, ptr %y, align 4 + // OPT0-SAME: !noundef + // OPT0: store i32 %[[TEMP2]], ptr %x, align 4 + // OPT0-NOT: memcpy + // OPT3-NOT: load + // OPT3: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 4, i1 false) + // CHECK: store i32 %[[TEMP]], ptr %y, align 4 + // CHECK: ret void + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_pair( +#[no_mangle] +pub unsafe fn swap_pair(x: &mut (i32, u32), y: &mut (i32, u32)) { + // CHECK-NOT: alloca + + // CHECK: load i32 + // CHECK-SAME: !noundef + // CHECK: load i32 + // CHECK-SAME: !noundef + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %x, ptr align 4 %y, i64 8, i1 false) + // CHECK: store i32 + // CHECK: store i32 + typed_swap(x, y) +} + +// CHECK-LABEL: @swap_str( +#[no_mangle] +pub unsafe fn swap_str<'a>(x: &mut &'a str, y: &mut &'a str) { + // CHECK-NOT: alloca + + // CHECK: load ptr + // CHECK-SAME: !nonnull + // CHECK-SAME: !noundef + // CHECK: load i64 + // CHECK-SAME: !noundef + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 16, i1 false) + // CHECK: store ptr + // CHECK: store i64 + typed_swap(x, y) +} + +// OPT0-LABEL: @swap_string( +#[no_mangle] +pub unsafe fn swap_string(x: &mut String, y: &mut String) { + // OPT0: %[[TEMP:.+]] = alloca {{.+}}, align 8 + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %[[TEMP]], ptr align 8 %x, i64 24, i1 false) + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %x, ptr align 8 %y, i64 24, i1 false) + // OPT0: call void @llvm.memcpy.p0.p0.i64(ptr align 8 %y, ptr align 8 %[[TEMP]], i64 24, i1 false) + typed_swap(x, y) +} diff --git a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs index 6f47f5e3355..1332338b26a 100644 --- a/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs +++ b/tests/codegen/sanitizer/cfi/emit-type-metadata-id-itanium-cxx-abi-repr-transparent-types.rs @@ -34,6 +34,12 @@ pub struct Bar(i32); #[repr(transparent)] pub struct Type3<T>(T); +// repr(transparent) wrapper which engages in self-reference +#[repr(transparent)] +pub struct Type4(Type4Helper<Type4>); +#[repr(transparent)] +pub struct Type4Helper<T>(*mut T); + pub fn foo1(_: Type1) { } // CHECK: define{{.*}}4foo1{{.*}}!type ![[TYPE1:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo2(_: Type1, _: Type1) { } @@ -52,6 +58,13 @@ pub fn foo8(_: Type3<Bar>, _: Type3<Bar>) { } // CHECK: define{{.*}}4foo8{{.*}}!type ![[TYPE8:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} pub fn foo9(_: Type3<Bar>, _: Type3<Bar>, _: Type3<Bar>) { } // CHECK: define{{.*}}4foo9{{.*}}!type ![[TYPE9:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo10(_: Type4) { } +// CHECK: define{{.*}}5foo10{{.*}}!type ![[TYPE10:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo11(_: Type4, _: Type4) { } +// CHECK: define{{.*}}5foo11{{.*}}!type ![[TYPE11:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} +pub fn foo12(_: Type4, _: Type4, _: Type4) { } +// CHECK: define{{.*}}5foo12{{.*}}!type ![[TYPE12:[0-9]+]] !type !{{[0-9]+}} !type !{{[0-9]+}} !type !{{[0-9]+}} + // CHECK: ![[TYPE1]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooE"} // CHECK: ![[TYPE2]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3FooS_E"} @@ -62,3 +75,6 @@ pub fn foo9(_: Type3<Bar>, _: Type3<Bar>, _: Type3<Bar>) { } // CHECK: ![[TYPE7]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarE"} // CHECK: ![[TYPE8]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_E"} // CHECK: ![[TYPE9]] = !{i64 0, !"_ZTSFvu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}3BarS_S_E"} +// CHECK: ![[TYPE10]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4E"} +// CHECK: ![[TYPE11]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4S0_E"} +// CHECK: ![[TYPE12]] = !{i64 0, !"_ZTSFvPu{{[0-9]+}}NtC{{[[:print:]]+}}_{{[[:print:]]+}}5Type4S0_S0_E"} diff --git a/tests/codegen/swap-small-types.rs b/tests/codegen/swap-small-types.rs index 5fdf4a5804a..4dcfed2a53a 100644 --- a/tests/codegen/swap-small-types.rs +++ b/tests/codegen/swap-small-types.rs @@ -70,10 +70,7 @@ pub fn swap_slices<'a>(x: &mut &'a [u32], y: &mut &'a [u32]) { // CHECK-NOT: alloca // CHECK: load ptr // CHECK: load i64 - // CHECK: load ptr - // CHECK: load i64 - // CHECK: store ptr - // CHECK: store i64 + // CHECK: call void @llvm.memcpy.p0.p0.i64({{.+}}, i64 16, i1 false) // CHECK: store ptr // CHECK: store i64 swap(x, y) diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff index a958e5541fa..21cf745b680 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff index b073e27729e..ee58a974480 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff index 0a9f339ddba..9fc9c8ed409 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff index bbc791148af..30d93347afd 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -60,7 +64,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff index 3a11677f6f0..3a46edbc849 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff index 9e7e08866b9..3c71214c35f 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff index beadfbc07b6..4557e7b26d6 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb4, otherwise: bb2]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff index 9ea86956b83..5ab2d5e0fdc 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff @@ -28,6 +28,10 @@ let mut _10: *mut (); let mut _11: *const [bool; 0]; scope 13 { + scope 14 (inlined core::ub_checks::check_language_ub) { + scope 15 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -62,7 +66,7 @@ StorageDead(_7); StorageLive(_11); StorageLive(_8); - _8 = UbCheck(LanguageUb); + _8 = UbChecks(); switchInt(move _8) -> [0: bb5, otherwise: bb3]; } diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs index 9986d903501..617501217cf 100644 --- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs +++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.rs @@ -1,6 +1,5 @@ //@ unit-test: DataflowConstProp //@ compile-flags: -Zmir-enable-passes=+GVN,+Inline -//@ ignore-debug assertions change the output MIR // EMIT_MIR_FOR_EACH_BIT_WIDTH // EMIT_MIR_FOR_EACH_PANIC_STRATEGY diff --git a/tests/mir-opt/funky_arms.rs b/tests/mir-opt/funky_arms.rs index 189cd7951fb..fc3691049eb 100644 --- a/tests/mir-opt/funky_arms.rs +++ b/tests/mir-opt/funky_arms.rs @@ -1,6 +1,5 @@ // skip-filecheck // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ compile-flags: --crate-type lib -Cdebug-assertions=no #![feature(flt2dec)] diff --git a/tests/mir-opt/inline/unchecked_shifts.rs b/tests/mir-opt/inline/unchecked_shifts.rs index 2fd18f3d5eb..12b00e76a11 100644 --- a/tests/mir-opt/inline/unchecked_shifts.rs +++ b/tests/mir-opt/inline/unchecked_shifts.rs @@ -2,7 +2,6 @@ #![crate_type = "lib"] #![feature(unchecked_shifts)] -//@ ignore-debug: the debug assertions prevent the inlining we are testing for //@ compile-flags: -Zmir-opt-level=2 -Zinline-mir // EMIT_MIR unchecked_shifts.unchecked_shl_unsigned_smaller.Inline.diff diff --git a/tests/mir-opt/inline/unwrap_unchecked.rs b/tests/mir-opt/inline/unwrap_unchecked.rs index e44e4e23a2c..13c76c5bb53 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.rs +++ b/tests/mir-opt/inline/unwrap_unchecked.rs @@ -1,8 +1,7 @@ #![crate_type = "lib"] // EMIT_MIR_FOR_EACH_PANIC_STRATEGY -//@ ignore-debug: the debug assertions prevent the inlining we are testing for -//@ compile-flags: -Zmir-opt-level=2 -Zinline-mir -Cdebug-assertions=no +//@ compile-flags: -Zmir-opt-level=2 -Zinline-mir // EMIT_MIR unwrap_unchecked.unwrap_unchecked.Inline.diff // EMIT_MIR unwrap_unchecked.unwrap_unchecked.PreCodegen.after.mir diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff index 6f7853a3e97..028040edc85 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-abort.diff @@ -17,6 +17,10 @@ + let _5: (); + scope 5 { + } ++ scope 6 (inlined core::ub_checks::check_language_ub) { ++ scope 7 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } + } + } @@ -37,7 +41,7 @@ + + bb2: { + StorageLive(_4); -+ _4 = UbCheck(LanguageUb); ++ _4 = UbChecks(); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff index cac06d4af08..484fd37248c 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.Inline.panic-unwind.diff @@ -17,6 +17,10 @@ + let _5: (); + scope 5 { + } ++ scope 6 (inlined core::ub_checks::check_language_ub) { ++ scope 7 (inlined core::ub_checks::check_language_ub::runtime) { ++ } ++ } + } + } + } @@ -41,7 +45,7 @@ - resume; + bb2: { + StorageLive(_4); -+ _4 = UbCheck(LanguageUb); ++ _4 = UbChecks(); + assume(_4); + _5 = unreachable_unchecked::precondition_check() -> [return: bb1, unwind unreachable]; + } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir index 5c611650154..9cd7053871e 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-abort.mir @@ -15,6 +15,10 @@ fn unwrap_unchecked(_1: Option<T>) -> T { let _4: (); scope 5 { } + scope 6 (inlined core::ub_checks::check_language_ub) { + scope 7 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -27,7 +31,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { bb1: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir index 5c611650154..9cd7053871e 100644 --- a/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/inline/unwrap_unchecked.unwrap_unchecked.PreCodegen.after.panic-unwind.mir @@ -15,6 +15,10 @@ fn unwrap_unchecked(_1: Option<T>) -> T { let _4: (); scope 5 { } + scope 6 (inlined core::ub_checks::check_language_ub) { + scope 7 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } } } @@ -27,7 +31,7 @@ fn unwrap_unchecked(_1: Option<T>) -> T { bb1: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs new file mode 100644 index 00000000000..be73bc49de5 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -0,0 +1,28 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// skip-filecheck +//@ unit-test: Inline +//@ edition: 2021 +//@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib + +pub async fn run(permit: ActionPermit<'_, ()>, ctx: &mut core::task::Context<'_>) { + run2(permit, ctx); +} + +// EMIT_MIR inline_coroutine_body.run2-{closure#0}.Inline.diff +fn run2<T>(permit: ActionPermit<'_, T>, ctx: &mut core::task::Context) { + _ = || { + let mut fut = ActionPermit::perform(permit); + let fut = unsafe { core::pin::Pin::new_unchecked(&mut fut) }; + _ = core::future::Future::poll(fut, ctx); + }; +} + +pub struct ActionPermit<'a, T> { + _guard: core::cell::Ref<'a, T>, +} + +impl<'a, T> ActionPermit<'a, T> { + async fn perform(self) { + core::future::ready(()).await + } +} diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff new file mode 100644 index 00000000000..b189b4e73f4 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -0,0 +1,281 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _41: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind unreachable]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb3, 1: bb13, 3: bb12, otherwise: bb8]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2: { ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind unreachable]; ++ drop(_2) -> [return: bb1, unwind unreachable]; + } + ++ bb3: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_41); ++ _41 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _41); ++ StorageDead(_41); ++ StorageDead(_14); ++ _12 = <std::future::Ready<()> as IntoFuture>::into_future(move _13) -> [return: bb4, unwind unreachable]; ++ } ++ + bb4: { +- StorageDead(_2); +- return; ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb5; ++ } ++ ++ bb5: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb6, unwind unreachable]; ++ } ++ ++ bb6: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = <std::future::Ready<()> as Future>::poll(move _19, move _22) -> [return: bb7, unwind unreachable]; ++ } ++ ++ bb7: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb10, 1: bb9, otherwise: bb8]; ++ } ++ ++ bb8: { ++ unreachable; ++ } ++ ++ bb9: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb2; ++ } ++ ++ bb10: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb11, unwind unreachable]; ++ } ++ ++ bb11: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb2; ++ } ++ ++ bb12: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb5; ++ } ++ ++ bb13: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb13, unwind unreachable]; + } + } + diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff new file mode 100644 index 00000000000..ed18c0a3adb --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -0,0 +1,341 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _41: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _42: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _43: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind: bb6]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind: bb5]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ StorageLive(_41); ++ StorageLive(_42); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb5, 1: bb22, 2: bb21, 3: bb20, otherwise: bb10]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2 (cleanup): { ++ drop(_2) -> [return: bb3, unwind terminate(cleanup)]; ++ } ++ ++ bb3 (cleanup): { ++ resume; ++ } ++ ++ bb4: { ++ StorageDead(_42); ++ StorageDead(_41); ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind: bb6]; ++ drop(_2) -> [return: bb1, unwind: bb3]; + } + +- bb4: { +- StorageDead(_2); +- return; ++ bb5: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_43); ++ _43 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _43); ++ StorageDead(_43); ++ StorageDead(_14); ++ _12 = <std::future::Ready<()> as IntoFuture>::into_future(move _13) -> [return: bb6, unwind: bb17]; + } + +- bb5 (cleanup): { +- drop(_2) -> [return: bb6, unwind terminate(cleanup)]; ++ bb6: { ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb7; + } + +- bb6 (cleanup): { +- resume; ++ bb7: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb8, unwind: bb15]; ++ } ++ ++ bb8: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = <std::future::Ready<()> as Future>::poll(move _19, move _22) -> [return: bb9, unwind: bb14]; ++ } ++ ++ bb9: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb12, 1: bb11, otherwise: bb10]; ++ } ++ ++ bb10: { ++ unreachable; ++ } ++ ++ bb11: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb4; ++ } ++ ++ bb12: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb13, unwind: bb19]; ++ } ++ ++ bb13: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb4; ++ } ++ ++ bb14 (cleanup): { ++ StorageDead(_22); ++ StorageDead(_19); ++ StorageDead(_23); ++ goto -> bb16; ++ } ++ ++ bb15 (cleanup): { ++ StorageDead(_20); ++ StorageDead(_19); ++ goto -> bb16; ++ } ++ ++ bb16 (cleanup): { ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ goto -> bb18; ++ } ++ ++ bb17 (cleanup): { ++ StorageDead(_13); ++ goto -> bb18; ++ } ++ ++ bb18 (cleanup): { ++ StorageDead(_12); ++ _41 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb19, unwind terminate(cleanup)]; ++ } ++ ++ bb19 (cleanup): { ++ _42 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_42)) = 2; ++ goto -> bb2; ++ } ++ ++ bb20: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb7; ++ } ++ ++ bb21: { ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb21, unwind: bb2]; ++ } ++ ++ bb22: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb22, unwind: bb2]; + } + } + diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs b/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs index 67540676f4a..561bafa9651 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -Zmir-opt-level=2 -Cdebuginfo=0 -//@ ignore-debug: standard library debug assertions add a panic that breaks this optimization #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir index 0597e453e22..455e4ba7244 100644 --- a/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir +++ b/tests/mir-opt/pre-codegen/duplicate_switch_targets.ub_if_b.PreCodegen.after.mir @@ -9,6 +9,10 @@ fn ub_if_b(_1: Thing) -> Thing { let _4: (); scope 2 { } + scope 3 (inlined core::ub_checks::check_language_ub) { + scope 4 (inlined core::ub_checks::check_language_ub::runtime) { + } + } } bb0: { @@ -23,7 +27,7 @@ fn ub_if_b(_1: Thing) -> Thing { bb2: { StorageLive(_3); - _3 = UbCheck(LanguageUb); + _3 = UbChecks(); assume(_3); _4 = unreachable_unchecked::precondition_check() -> [return: bb3, unwind unreachable]; } diff --git a/tests/mir-opt/pre-codegen/mem_replace.rs b/tests/mir-opt/pre-codegen/mem_replace.rs index 9cb3a839956..a68fe31f609 100644 --- a/tests/mir-opt/pre-codegen/mem_replace.rs +++ b/tests/mir-opt/pre-codegen/mem_replace.rs @@ -1,6 +1,6 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -Zinline-mir -//@ ignore-debug the standard library debug assertions leak into this test +//@ ignore-debug: precondition checks on ptr::read/write are under cfg(debug_assertions) // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/slice_index.rs b/tests/mir-opt/pre-codegen/slice_index.rs index 1d977ee9214..c9dd72d8be2 100644 --- a/tests/mir-opt/pre-codegen/slice_index.rs +++ b/tests/mir-opt/pre-codegen/slice_index.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -//@ ignore-debug the standard library debug assertions leak into this test // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/mir-opt/pre-codegen/slice_iter.rs b/tests/mir-opt/pre-codegen/slice_iter.rs index 0fbd3706544..86f37ca4d13 100644 --- a/tests/mir-opt/pre-codegen/slice_iter.rs +++ b/tests/mir-opt/pre-codegen/slice_iter.rs @@ -1,6 +1,5 @@ // skip-filecheck //@ compile-flags: -O -C debuginfo=0 -Zmir-opt-level=2 -//@ ignore-debug the standard library debug assertions leak into this test // EMIT_MIR_FOR_EACH_PANIC_STRATEGY #![crate_type = "lib"] diff --git a/tests/run-make/rustdoc-test-args/foo.rs b/tests/run-make/rustdoc-test-args/foo.rs new file mode 100644 index 00000000000..51d17849fd7 --- /dev/null +++ b/tests/run-make/rustdoc-test-args/foo.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs new file mode 100644 index 00000000000..808d13928eb --- /dev/null +++ b/tests/run-make/rustdoc-test-args/rmake.rs @@ -0,0 +1,18 @@ +extern crate run_make_support; + +use run_make_support::{out_dir, rustdoc}; +use std::{fs, iter}; +use std::path::Path; + +fn generate_a_lot_of_cfgs(path: &Path) { + let content = iter::repeat("--cfg=a\n").take(100_000).collect::<String>(); + fs::write(path, content.as_bytes()).expect("failed to create args file"); +} + +fn main() { + let arg_file = out_dir().join("args"); + generate_a_lot_of_cfgs(&arg_file); + + let arg_file = format!("@{}", arg_file.display()); + rustdoc().arg("--test").arg(&arg_file).arg("foo.rs").run(); +} diff --git a/tests/ui/asm/aarch64/type-check-3.stderr b/tests/ui/asm/aarch64/type-check-3.stderr index f710df2dcde..9e37bb4c203 100644 --- a/tests/ui/asm/aarch64/type-check-3.stderr +++ b/tests/ui/asm/aarch64/type-check-3.stderr @@ -4,8 +4,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0u8); | ^^ --- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument @@ -14,8 +14,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0u16); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:52:15 @@ -23,8 +23,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:54:15 @@ -32,8 +32,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0f32); | ^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:57:15 @@ -41,8 +41,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0i16); | ^^ ---- for this argument | - = help: use `{0:h}` to have the register formatted as `h0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:h}` to have the register formatted as `h0` (for 16-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:59:15 @@ -50,8 +50,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0f32); | ^^ ---- for this argument | - = help: use `{0:s}` to have the register formatted as `s0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:s}` to have the register formatted as `s0` (for 32-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:61:15 @@ -59,8 +59,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg) 0f64); | ^^ ---- for this argument | - = help: use `{0:d}` to have the register formatted as `d0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:d}` to have the register formatted as `d0` (for 64-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:63:15 @@ -68,8 +68,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(vreg_low16) 0f64); | ^^ ---- for this argument | - = help: use `{0:d}` to have the register formatted as `d0` - = help: or use `{0:v}` to keep the default formatting of `v0` + = help: use `{0:d}` to have the register formatted as `d0` (for 64-bit values) + = help: or use `{0:v}` to keep the default formatting of `v0` (for 128-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:66:15 @@ -77,8 +77,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:68:15 @@ -86,8 +86,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) error: type `i128` cannot be used with this register class --> $DIR/type-check-3.rs:73:28 diff --git a/tests/ui/asm/bad-template.aarch64.stderr b/tests/ui/asm/bad-template.aarch64.stderr index b18946d7c6d..5023cf317d7 100644 --- a/tests/ui/asm/bad-template.aarch64.stderr +++ b/tests/ui/asm/bad-template.aarch64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:w}` to have the register formatted as `w0` - = help: or use `{0:x}` to keep the default formatting of `x0` + = help: use `{0:w}` to have the register formatted as `w0` (for 32-bit values) + = help: or use `{0:x}` to keep the default formatting of `x0` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/bad-template.x86_64.stderr b/tests/ui/asm/bad-template.x86_64.stderr index 2f584c30a32..1b9775636f5 100644 --- a/tests/ui/asm/bad-template.x86_64.stderr +++ b/tests/ui/asm/bad-template.x86_64.stderr @@ -194,8 +194,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{:foo}", in(reg) foo); | ^^^^^^ --- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default error: aborting due to 21 previous errors; 1 warning emitted diff --git a/tests/ui/asm/x86_64/type-check-3.stderr b/tests/ui/asm/x86_64/type-check-3.stderr index 1baf50ff6e0..34bfcd71cac 100644 --- a/tests/ui/asm/x86_64/type-check-3.stderr +++ b/tests/ui/asm/x86_64/type-check-3.stderr @@ -44,8 +44,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0}", in(reg) 0i16); | ^^^ ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) = note: `#[warn(asm_sub_register)]` on by default warning: formatting may not be suitable for sub-register argument @@ -54,8 +54,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{0} {0:x}", in(reg) 0i16); | ^^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `ax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:x}` to have the register formatted as `ax` (for 16-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:38:15 @@ -63,8 +63,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(reg) 0i32); | ^^ ---- for this argument | - = help: use `{0:e}` to have the register formatted as `eax` - = help: or use `{0:r}` to keep the default formatting of `rax` + = help: use `{0:e}` to have the register formatted as `eax` (for 32-bit values) + = help: or use `{0:r}` to keep the default formatting of `rax` (for 64-bit values) warning: formatting may not be suitable for sub-register argument --> $DIR/type-check-3.rs:41:15 @@ -72,8 +72,8 @@ warning: formatting may not be suitable for sub-register argument LL | asm!("{}", in(ymm_reg) 0i64); | ^^ ---- for this argument | - = help: use `{0:x}` to have the register formatted as `xmm0` - = help: or use `{0:y}` to keep the default formatting of `ymm0` + = help: use `{0:x}` to have the register formatted as `xmm0` (for 128-bit values) + = help: or use `{0:y}` to keep the default formatting of `ymm0` (for 256-bit values) error: type `i8` cannot be used with this register class --> $DIR/type-check-3.rs:52:28 diff --git a/tests/ui/associated-type-bounds/auxiliary/implied-predicates.rs b/tests/ui/associated-type-bounds/auxiliary/implied-predicates.rs new file mode 100644 index 00000000000..fe74c64fbe2 --- /dev/null +++ b/tests/ui/associated-type-bounds/auxiliary/implied-predicates.rs @@ -0,0 +1,7 @@ +pub trait Bar: Super<SuperAssoc: Bound> {} + +pub trait Super { + type SuperAssoc; +} + +pub trait Bound {} diff --git a/tests/ui/associated-type-bounds/implied-predicates.rs b/tests/ui/associated-type-bounds/implied-predicates.rs new file mode 100644 index 00000000000..e97d7a396c4 --- /dev/null +++ b/tests/ui/associated-type-bounds/implied-predicates.rs @@ -0,0 +1,9 @@ +//@ aux-build:implied-predicates.rs +//@ check-pass + +extern crate implied_predicates; +use implied_predicates::Bar; + +fn bar<B: Bar>() {} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/resolution-failure-building-vtable-representation-ice-90691.rs b/tests/ui/associated-type-bounds/resolution-failure-building-vtable-representation-ice-90691.rs new file mode 100644 index 00000000000..d3c13974127 --- /dev/null +++ b/tests/ui/associated-type-bounds/resolution-failure-building-vtable-representation-ice-90691.rs @@ -0,0 +1,42 @@ +// ICE #90691 Encountered error `Unimplemented` selecting ... +//@ build-pass +// issue: rust-lang/rust#90691 + +trait TError: std::fmt::Debug {} +impl TError for () {} + +trait SuperTrait { + type Error; +} + +trait Trait: SuperTrait<Error: TError> {} + +impl<T> Trait for T +where + T: SuperTrait, + <T as SuperTrait>::Error: TError, +{ +} + +struct SomeTrait<S>(S); +struct BoxedTrait(Box<dyn Trait<Error = ()>>); + +impl<S: 'static> From<SomeTrait<S>> for BoxedTrait { + fn from(other: SomeTrait<S>) -> Self { + Self(Box::new(other)) + } +} + +impl<S> SuperTrait for SomeTrait<S> { + type Error = (); +} + +impl From<()> for BoxedTrait { + fn from(c: ()) -> Self { + Self::from(SomeTrait(c)) + } +} + +fn main() { + let _: BoxedTrait = ().into(); +} diff --git a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout index 47b39e5246d..d6fb643702c 100644 --- a/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout +++ b/tests/ui/async-await/future-sizes/async-awaiting-fut.stdout @@ -2,7 +2,7 @@ print-type-size type: `{async fn body@$DIR/async-awaiting-fut.rs:21:21: 24:2}`: print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Suspend0`: 3077 bytes -print-type-size local `.__awaitee`: 3077 bytes +print-type-size local `.__awaitee`: 3077 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/async-awaiting-fut.rs:10:64: 19:2}>`: 3077 bytes, alignment: 1 bytes @@ -19,19 +19,19 @@ print-type-size variant `Suspend0`: 2052 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1 bytes print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes -print-type-size local `..coroutine_field4`: 1 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `..coroutine_field4`: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19} print-type-size variant `Suspend1`: 3076 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1026 bytes -print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes -print-type-size local `.__awaitee`: 1025 bytes +print-type-size local `..coroutine_field4`: 1 bytes, alignment: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:8:35: 8:37} print-type-size variant `Suspend2`: 2052 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size padding: 1 bytes print-type-size local `.fut`: 1025 bytes, alignment: 1 bytes -print-type-size local `..coroutine_field4`: 1 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `..coroutine_field4`: 1 bytes, type: bool +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async-awaiting-fut.rs:6:17: 6:19} print-type-size variant `Returned`: 1025 bytes print-type-size upvar `.fut`: 1025 bytes, offset: 0 bytes, alignment: 1 bytes print-type-size variant `Panicked`: 1025 bytes diff --git a/tests/ui/async-await/future-sizes/large-arg.stdout b/tests/ui/async-await/future-sizes/large-arg.stdout index 005460df626..589df102af4 100644 --- a/tests/ui/async-await/future-sizes/large-arg.stdout +++ b/tests/ui/async-await/future-sizes/large-arg.stdout @@ -2,7 +2,7 @@ print-type-size type: `{async fn body@$DIR/large-arg.rs:6:21: 8:2}`: 3076 bytes, print-type-size discriminant: 1 bytes print-type-size variant `Unresumed`: 0 bytes print-type-size variant `Suspend0`: 3075 bytes -print-type-size local `.__awaitee`: 3075 bytes +print-type-size local `.__awaitee`: 3075 bytes, type: {async fn body@$DIR/large-arg.rs:10:30: 12:2} print-type-size variant `Returned`: 0 bytes print-type-size variant `Panicked`: 0 bytes print-type-size type: `std::mem::ManuallyDrop<{async fn body@$DIR/large-arg.rs:10:30: 12:2}>`: 3075 bytes, alignment: 1 bytes @@ -17,7 +17,7 @@ print-type-size variant `Unresumed`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Suspend0`: 3074 bytes print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 2050 bytes +print-type-size local `.__awaitee`: 2050 bytes, type: {async fn body@$DIR/large-arg.rs:13:26: 15:2} print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes @@ -34,7 +34,7 @@ print-type-size variant `Unresumed`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Suspend0`: 2049 bytes print-type-size upvar `.t`: 1024 bytes -print-type-size local `.__awaitee`: 1025 bytes +print-type-size local `.__awaitee`: 1025 bytes, type: {async fn body@$DIR/large-arg.rs:16:26: 18:2} print-type-size variant `Returned`: 1024 bytes print-type-size upvar `.t`: 1024 bytes print-type-size variant `Panicked`: 1024 bytes diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs new file mode 100644 index 00000000000..b9e729bff62 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs @@ -0,0 +1,26 @@ +// issue: rust-lang/rust#104779 +// ICE region infer, IndexMap: key not found + +struct Inv<'a>(&'a mut &'a ()); +enum Foo<T> { + Bar, + Var(T), +} +type Subtype = Foo<for<'a, 'b> fn(Inv<'a>, Inv<'b>)>; +type Supertype = Foo<for<'a> fn(Inv<'a>, Inv<'a>)>; + +fn foo() -> impl Sized { +//~^ WARN function cannot return without recursing + loop { + match foo() { + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Subtype::Bar => (), + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Supertype::Var(x) => {} + } + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr new file mode 100644 index 00000000000..887cb14a769 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr @@ -0,0 +1,42 @@ +warning: function cannot return without recursing + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:12:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | match foo() { + | ----- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors; 1 warning emitted + diff --git a/tests/ui/const-generics/generic_const_exprs/failed-to-normalize-ice-issue-88421.rs b/tests/ui/const-generics/generic_const_exprs/failed-to-normalize-ice-issue-88421.rs new file mode 100644 index 00000000000..3d41eeeff45 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/failed-to-normalize-ice-issue-88421.rs @@ -0,0 +1,36 @@ +//@ check-pass +// issue: rust-lang/rust#88421 +#![feature(adt_const_params)] +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +use std::ops::Index; + +pub struct CellPossibilities; + +pub enum CellState<const SQUARE_SIZE: usize> { + Empty(Option<CellPossibilities>), +} + +pub struct Sudoku<const SQUARE_SIZE: usize>; + +impl<const SQUARE_SIZE: usize> Sudoku<SQUARE_SIZE>where + [CellState<SQUARE_SIZE>; SQUARE_SIZE * SQUARE_SIZE]: Sized, +{ + pub fn random() { + let CellState::Empty(_) = Self[()]; + } +} + +impl<const SQUARE_SIZE: usize> Index<()> for Sudoku<SQUARE_SIZE> +where + [CellState<SQUARE_SIZE>; SQUARE_SIZE * SQUARE_SIZE]: Sized, +{ + type Output = CellState<SQUARE_SIZE>; + + fn index(&self, _: ()) -> &Self::Output { + todo!() + } +} + +pub fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs new file mode 100644 index 00000000000..ed5ba32b621 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs @@ -0,0 +1,57 @@ +// issue: rust-lang/rust#106423 +// ICE collection encountered polymorphic constant: UnevaluatedConst {..} +//@ edition:2021 +//@ check-pass + +#![feature(generic_const_exprs, generic_arg_infer)] +#![allow(incomplete_features)] +#![allow(unused)] + +use core::mem::MaybeUninit; + +pub struct Arr<T, const N: usize> { + v: [MaybeUninit<T>; N], +} + +impl<T, const N: usize> Arr<T, N> { + const ELEM: MaybeUninit<T> = MaybeUninit::uninit(); + const INIT: [MaybeUninit<T>; N] = [Self::ELEM; N]; // important for optimization of `new` + + fn new() -> Self { + Arr { v: Self::INIT } + } +} + +pub struct BaFormatFilter<const N: usize> {} + +pub enum DigitalFilter<const N: usize> +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + Ba(BaFormatFilter<{ N * 2 + 1 }>), +} + +pub fn iirfilter_st_copy<const N: usize, const M: usize>(_: [f32; M]) -> DigitalFilter<N> +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + let zpk = zpk2tf_st(&Arr::<f32, { N * 2 }>::new(), &Arr::<f32, { N * 2 }>::new()); + DigitalFilter::Ba(zpk) +} + +pub fn zpk2tf_st<const N: usize>( + _z: &Arr<f32, N>, + _p: &Arr<f32, N>, +) -> BaFormatFilter<{ N + 1 }> +where + [(); N + 1]: Sized, +{ + BaFormatFilter {} +} + + +fn main() { + iirfilter_st_copy::<4, 2>([10., 50.,]); +} diff --git a/tests/ui/consts/ice-bad-input-type-for-cast-83056.rs b/tests/ui/consts/ice-bad-input-type-for-cast-83056.rs new file mode 100644 index 00000000000..1a22dc2b549 --- /dev/null +++ b/tests/ui/consts/ice-bad-input-type-for-cast-83056.rs @@ -0,0 +1,7 @@ +// #83056 ICE "bad input type for cast" +// issue: rust-lang/rust#83056 + +struct S([bool; f as usize]); +fn f() -> T {} +//~^ ERROR cannot find type `T` in this scope +pub fn main() {} diff --git a/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr b/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr new file mode 100644 index 00000000000..115f1688520 --- /dev/null +++ b/tests/ui/consts/ice-bad-input-type-for-cast-83056.stderr @@ -0,0 +1,20 @@ +error[E0412]: cannot find type `T` in this scope + --> $DIR/ice-bad-input-type-for-cast-83056.rs:5:11 + | +LL | struct S([bool; f as usize]); + | ----------------------------- similarly named struct `S` defined here +LL | fn f() -> T {} + | ^ + | +help: a struct with a similar name exists + | +LL | fn f() -> S {} + | ~ +help: you might be missing a type parameter + | +LL | fn f<T>() -> T {} + | +++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0412`. diff --git a/tests/ui/consts/missing_span_in_backtrace.stderr b/tests/ui/consts/missing_span_in_backtrace.stderr index 3e3e8e976be..9e0506e7e38 100644 --- a/tests/ui/consts/missing_span_in_backtrace.stderr +++ b/tests/ui/consts/missing_span_in_backtrace.stderr @@ -5,8 +5,6 @@ error[E0080]: evaluation of constant value failed | note: inside `std::ptr::read::<MaybeUninit<MaybeUninit<u8>>>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL -note: inside `mem::swap_simple::<MaybeUninit<MaybeUninit<u8>>>` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL note: inside `std::ptr::swap_nonoverlapping_simple_untyped::<MaybeUninit<u8>>` --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL note: inside `swap_nonoverlapping::<MaybeUninit<u8>>` diff --git a/tests/ui/drop/norm-ice-106444.rs b/tests/ui/drop/norm-ice-106444.rs new file mode 100644 index 00000000000..b248bc73bbe --- /dev/null +++ b/tests/ui/drop/norm-ice-106444.rs @@ -0,0 +1,16 @@ +// issue: rust-lang/rust#106444 +// ICE failed to normalize +//@ compile-flags: -Zmir-opt-level=3 +//@ check-pass + +#![crate_type="lib"] + +pub trait A { + type B; +} + +pub struct S<T: A>(T::B); + +pub fn foo<T: A>(p: *mut S<T>) { + unsafe { core::ptr::drop_in_place(p) }; +} diff --git a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs new file mode 100644 index 00000000000..bba7190d43d --- /dev/null +++ b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.rs @@ -0,0 +1,16 @@ +// test for ICE when casting extern "C" fn when it has a non-FFI-safe argument +// issue: rust-lang/rust#52334 +//@ check-pass +//@ normalize-stderr-test "\[i8\]" -> "[i8 or u8 (arch dependant)]" +//@ normalize-stderr-test "\[u8\]" -> "[i8 or u8 (arch dependant)]" + +type Foo = extern "C" fn(::std::ffi::CStr); +//~^ WARN `extern` fn uses type +extern "C" { + fn meh(blah: Foo); + //~^ WARN `extern` block uses type +} + +fn main() { + meh as usize; +} diff --git a/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr new file mode 100644 index 00000000000..83492988479 --- /dev/null +++ b/tests/ui/extern/extern-C-non-FFI-safe-arg-ice-52334.stderr @@ -0,0 +1,22 @@ +warning: `extern` fn uses type `[i8 or u8 (arch dependant)]`, which is not FFI-safe + --> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:7:12 + | +LL | type Foo = extern "C" fn(::std::ffi::CStr); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` block uses type `[i8 or u8 (arch dependant)]`, which is not FFI-safe + --> $DIR/extern-C-non-FFI-safe-arg-ice-52334.rs:10:18 + | +LL | fn meh(blah: Foo); + | ^^^ not FFI-safe + | + = help: consider using a raw pointer instead + = note: slices have no C equivalent + = note: `#[warn(improper_ctypes)]` on by default + +warning: 2 warnings emitted + diff --git a/tests/ui/extern/extern-C-str-arg-ice-80125.rs b/tests/ui/extern/extern-C-str-arg-ice-80125.rs new file mode 100644 index 00000000000..0908d6199ef --- /dev/null +++ b/tests/ui/extern/extern-C-str-arg-ice-80125.rs @@ -0,0 +1,15 @@ +// issue: rust-lang/rust#80125 +//@ check-pass +type ExternCallback = extern "C" fn(*const u8, u32, str); +//~^ WARN `extern` fn uses type `str`, which is not FFI-safe + +pub struct Struct(ExternCallback); + +#[no_mangle] +pub extern "C" fn register_something(bind: ExternCallback) -> Struct { +//~^ WARN `extern` fn uses type `str`, which is not FFI-safe +//~^^ WARN `extern` fn uses type `Struct`, which is not FFI-safe + Struct(bind) +} + +fn main() {} diff --git a/tests/ui/extern/extern-C-str-arg-ice-80125.stderr b/tests/ui/extern/extern-C-str-arg-ice-80125.stderr new file mode 100644 index 00000000000..ebd6cec6ecd --- /dev/null +++ b/tests/ui/extern/extern-C-str-arg-ice-80125.stderr @@ -0,0 +1,35 @@ +warning: `extern` fn uses type `str`, which is not FFI-safe + --> $DIR/extern-C-str-arg-ice-80125.rs:3:23 + | +LL | type ExternCallback = extern "C" fn(*const u8, u32, str); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: string slices have no C equivalent + = note: `#[warn(improper_ctypes_definitions)]` on by default + +warning: `extern` fn uses type `str`, which is not FFI-safe + --> $DIR/extern-C-str-arg-ice-80125.rs:9:44 + | +LL | pub extern "C" fn register_something(bind: ExternCallback) -> Struct { + | ^^^^^^^^^^^^^^ not FFI-safe + | + = help: consider using `*const u8` and a length instead + = note: string slices have no C equivalent + +warning: `extern` fn uses type `Struct`, which is not FFI-safe + --> $DIR/extern-C-str-arg-ice-80125.rs:9:63 + | +LL | pub extern "C" fn register_something(bind: ExternCallback) -> Struct { + | ^^^^^^ not FFI-safe + | + = help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct + = note: this struct has unspecified layout +note: the type is defined here + --> $DIR/extern-C-str-arg-ice-80125.rs:6:1 + | +LL | pub struct Struct(ExternCallback); + | ^^^^^^^^^^^^^^^^^ + +warning: 3 warnings emitted + diff --git a/tests/ui/higher-ranked/structually-relate-aliases.rs b/tests/ui/higher-ranked/structually-relate-aliases.rs new file mode 100644 index 00000000000..8df24702811 --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.rs @@ -0,0 +1,17 @@ +// regression test for issue #121649. + +trait ToUnit<'a> { + type Unit; +} + +trait Overlap<T> {} + +type Assoc<'a, T> = <T as ToUnit<'a>>::Unit; + +impl<T> Overlap<T> for T {} + +impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} +//~^ ERROR 13:17: 13:49: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] +//~| ERROR 13:36: 13:48: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] + +fn main() {} diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr new file mode 100644 index 00000000000..59fab52b221 --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -0,0 +1,27 @@ +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:36 + | +LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:17 + | +LL | impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl<T: for<'a> ToUnit<'a>> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs index 6f0dbd752b0..21e2fda1c3a 100644 --- a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs +++ b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.rs @@ -1,5 +1,4 @@ -struct Wrapper<'rom>(T); -//~^ ERROR cannot find type `T` in this scope +struct Wrapper<'rom>(&'rom ()); trait Foo { fn bar() -> Wrapper<impl Sized>; @@ -15,4 +14,18 @@ impl Foo for () { } } +trait Bar { + fn foo() -> Wrapper<impl Sized>; + //~^ ERROR missing lifetime specifier + //~| ERROR struct takes 0 generic arguments but 1 generic argument was supplied +} + +impl Bar for () { + fn foo() -> Wrapper<impl Sized> { + //~^ ERROR missing lifetime specifier + //~| ERROR struct takes 0 generic arguments but 1 generic argument was supplied + Wrapper(&()) + } +} + fn main() {} diff --git a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr index d30557c8a7b..d7fc40fa342 100644 --- a/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr +++ b/tests/ui/impl-trait/in-trait/opaque-and-lifetime-mismatch.stderr @@ -1,5 +1,5 @@ error[E0106]: missing lifetime specifier - --> $DIR/opaque-and-lifetime-mismatch.rs:5:24 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:24 | LL | fn bar() -> Wrapper<impl Sized>; | ^ expected named lifetime parameter @@ -10,19 +10,32 @@ help: consider using the `'static` lifetime, but this is uncommon unless you're LL | fn bar() -> Wrapper<'static, impl Sized>; | ++++++++ -error[E0412]: cannot find type `T` in this scope - --> $DIR/opaque-and-lifetime-mismatch.rs:1:22 +error[E0106]: missing lifetime specifier + --> $DIR/opaque-and-lifetime-mismatch.rs:18:24 + | +LL | fn foo() -> Wrapper<impl Sized>; + | ^ expected named lifetime parameter + | + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values + | +LL | fn foo() -> Wrapper<'static, impl Sized>; + | ++++++++ + +error[E0106]: missing lifetime specifier + --> $DIR/opaque-and-lifetime-mismatch.rs:24:24 | -LL | struct Wrapper<'rom>(T); - | ^ not found in this scope +LL | fn foo() -> Wrapper<impl Sized> { + | ^ expected named lifetime parameter | -help: you might be missing a type parameter + = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from +help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values | -LL | struct Wrapper<'rom, T>(T); - | +++ +LL | fn foo() -> Wrapper<'static, impl Sized> { + | ++++++++ error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied - --> $DIR/opaque-and-lifetime-mismatch.rs:5:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:17 | LL | fn bar() -> Wrapper<impl Sized>; | ^^^^^^^ ---------- help: remove this generic argument @@ -32,11 +45,25 @@ LL | fn bar() -> Wrapper<impl Sized>; note: struct defined here, with 0 generic parameters --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 | -LL | struct Wrapper<'rom>(T); +LL | struct Wrapper<'rom>(&'rom ()); + | ^^^^^^^ + +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/opaque-and-lifetime-mismatch.rs:18:17 + | +LL | fn foo() -> Wrapper<impl Sized>; + | ^^^^^^^ ---------- help: remove this generic argument + | | + | expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 + | +LL | struct Wrapper<'rom>(&'rom ()); | ^^^^^^^ error[E0053]: method `bar` has an incompatible return type for trait - --> $DIR/opaque-and-lifetime-mismatch.rs:11:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:10:17 | LL | fn bar() -> i32 { | ^^^ @@ -45,7 +72,7 @@ LL | fn bar() -> i32 { | return type in trait error[E0053]: method `bar` has an incompatible type for trait - --> $DIR/opaque-and-lifetime-mismatch.rs:11:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:10:17 | LL | fn bar() -> i32 { | ^^^ @@ -54,14 +81,28 @@ LL | fn bar() -> i32 { | help: change the output type to match the trait: `Wrapper<'static>` | note: type in trait - --> $DIR/opaque-and-lifetime-mismatch.rs:5:17 + --> $DIR/opaque-and-lifetime-mismatch.rs:4:17 | LL | fn bar() -> Wrapper<impl Sized>; | ^^^^^^^^^^^^^^^^^^^ = note: expected signature `fn() -> Wrapper<'static>` found signature `fn() -> i32` -error: aborting due to 5 previous errors +error[E0107]: struct takes 0 generic arguments but 1 generic argument was supplied + --> $DIR/opaque-and-lifetime-mismatch.rs:24:17 + | +LL | fn foo() -> Wrapper<impl Sized> { + | ^^^^^^^ ---------- help: remove this generic argument + | | + | expected 0 generic arguments + | +note: struct defined here, with 0 generic parameters + --> $DIR/opaque-and-lifetime-mismatch.rs:1:8 + | +LL | struct Wrapper<'rom>(&'rom ()); + | ^^^^^^^ + +error: aborting due to 8 previous errors -Some errors have detailed explanations: E0053, E0106, E0107, E0412. +Some errors have detailed explanations: E0053, E0106, E0107. For more information about an error, try `rustc --explain E0053`. diff --git a/tests/ui/impl-trait/recursive-ice-101862.rs b/tests/ui/impl-trait/recursive-ice-101862.rs new file mode 100644 index 00000000000..02f95fe5604 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.rs @@ -0,0 +1,12 @@ +// issue: rust-lang/rust#101852 +// ICE opaque type with non-universal region substs + +pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { +//~^ WARN function cannot return without recursing + vec![].append(&mut ice(x.as_ref())); + //~^ ERROR expected generic type parameter, found `&str` + + Vec::new() +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr new file mode 100644 index 00000000000..f4148720c33 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.stderr @@ -0,0 +1,24 @@ +warning: function cannot return without recursing + --> $DIR/recursive-ice-101862.rs:4:1 + | +LL | pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | --------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0792]: expected generic type parameter, found `&str` + --> $DIR/recursive-ice-101862.rs:6:5 + | +LL | pub fn ice(x: impl AsRef<str>) -> impl IntoIterator<Item = ()> { + | --------------- this generic parameter must be used with a generic type parameter +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0792`. diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 216f4cda698..4ca71c5f067 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"""'; | ^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; | ~~~~~~~~ @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let _: &str = '\"\"\"'; | ^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; - | ~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/str-as-char.rs:10:19 @@ -26,7 +26,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"\"\"\\"\\"'; | ^^^^^^^^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\\"\\"\\\""; | ~~~~~~~~~~~~~~~~~~~~ @@ -39,10 +39,10 @@ LL | let _: &str = 'a'; | | | expected due to this | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "a"; - | ~~~ + | ~ ~ error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-23589.stderr b/tests/ui/issues/issue-23589.stderr index 1a91f5e04db..21d383b0e8c 100644 --- a/tests/ui/issues/issue-23589.stderr +++ b/tests/ui/issues/issue-23589.stderr @@ -15,10 +15,10 @@ error[E0308]: mismatched types LL | let v: Vec(&str) = vec!['1', '2']; | ^^^ expected `&str`, found `char` | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let v: Vec(&str) = vec!["1", '2']; - | ~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-2.stderr b/tests/ui/lexer/lex-bad-char-literals-2.stderr index 1518a37ab53..76cde00404a 100644 --- a/tests/ui/lexer/lex-bad-char-literals-2.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-2.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | 'nope' | ^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | "nope" - | ~~~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-char-literals-3.stderr b/tests/ui/lexer/lex-bad-char-literals-3.stderr index 62a5e424cb4..3f339b2ef7d 100644 --- a/tests/ui/lexer/lex-bad-char-literals-3.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-3.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | static c: char = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "●●"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-3.rs:5:20 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let ch: &str = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "●●"; - | ~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-5.stderr b/tests/ui/lexer/lex-bad-char-literals-5.stderr index 184817a6579..8004157e87f 100644 --- a/tests/ui/lexer/lex-bad-char-literals-5.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-5.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | static c: char = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-5.rs:5:20 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let ch: &str = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-6.stderr b/tests/ui/lexer/lex-bad-char-literals-6.stderr index 2fe30304a50..96d409d59bb 100644 --- a/tests/ui/lexer/lex-bad-char-literals-6.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-6.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | let x: &str = 'ab'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let x: &str = "ab"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:4:19 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let y: char = 'cd'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let y: char = "cd"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:6:13 @@ -26,10 +26,10 @@ error: character literal may only contain one codepoint LL | let z = 'ef'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let z = "ef"; - | ~~~~ + | ~ ~ error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:13:20 diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed new file mode 100644 index 00000000000..b12139b0b40 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!("1 + 1"); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs new file mode 100644 index 00000000000..6548792f33b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!('1 + 1'); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr new file mode 100644 index 00000000000..57c5f82704e --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -0,0 +1,20 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:20 + | +LL | println!('1 + 1'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("1 + 1"); + | ~ ~ + +error: lifetimes cannot start with a number + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:14 + | +LL | println!('1 + 1'); + | ^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed new file mode 100644 index 00000000000..3ccec537c6c --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(" 1 + 1"); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs new file mode 100644 index 00000000000..8af72e47dbb --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(' 1 + 1'); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr new file mode 100644 index 00000000000..f64761af641 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -0,0 +1,13 @@ +error: character literal may only contain one codepoint + --> $DIR/lex-bad-str-literal-as-char-2.rs:3:14 + | +LL | println!(' 1 + 1'); + | ^^^^^^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!(" 1 + 1"); + | ~ ~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs new file mode 100644 index 00000000000..0ae227da5f1 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -0,0 +1,7 @@ +//@ revisions: rust2015 rust2018 rust2021 +//@[rust2018] edition:2018 +//@[rust2021] edition:2021 +fn main() { + println!('hello world'); + //[rust2015,rust2018,rust2021]~^ ERROR unterminated character literal +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr new file mode 100644 index 00000000000..06f12742667 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/nll/ice-106874.rs b/tests/ui/nll/ice-106874.rs new file mode 100644 index 00000000000..9337eee961b --- /dev/null +++ b/tests/ui/nll/ice-106874.rs @@ -0,0 +1,48 @@ +// issue: rust-lang/rust#106874 +// ICE BoundUniversalRegionError + +use std::marker::PhantomData; +use std::rc::Rc; + +pub fn func<V, F: Fn(&mut V)>(f: F) -> A<impl X> { + A(B(C::new(D::new(move |st| f(st))))) + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR higher-ranked subtype error + //~| ERROR higher-ranked subtype error +} + +trait X {} +trait Y { + type V; +} + +struct A<T>(T); + +struct B<T>(Rc<T>); +impl<T> X for B<T> {} + +struct C<T: Y>(T::V); +impl<T: Y> C<T> { + fn new(_: T) -> Rc<Self> { + todo!() + } +} +struct D<V, F>(F, PhantomData<fn(&mut V)>); + +impl<V, F> D<V, F> { + fn new(_: F) -> Self { + todo!() + } +} +impl<V, F: Fn(&mut V)> Y for D<V, F> { + type V = V; +} + +pub fn main() {} diff --git a/tests/ui/nll/ice-106874.stderr b/tests/ui/nll/ice-106874.stderr new file mode 100644 index 00000000000..ead4d490a62 --- /dev/null +++ b/tests/ui/nll/ice-106874.stderr @@ -0,0 +1,90 @@ +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors + diff --git a/tests/ui/panics/panic-in-message-fmt.rs b/tests/ui/panics/panic-in-message-fmt.rs new file mode 100644 index 00000000000..e5bedf96b35 --- /dev/null +++ b/tests/ui/panics/panic-in-message-fmt.rs @@ -0,0 +1,25 @@ +// Checks what happens when formatting the panic message panics. + +//@ run-fail +//@ exec-env:RUST_BACKTRACE=0 +//@ check-run-results +//@ error-pattern: panicked while processing panic +//@ normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> "" +//@ normalize-stderr-test: "\n +at [^\n]+" -> "" +//@ normalize-stderr-test: "(core/src/panicking\.rs):[0-9]+:[0-9]+" -> "$1:$$LINE:$$COL" +//@ ignore-emscripten "RuntimeError" junk in output + +use std::fmt::{Display, self}; + +struct MyStruct; + +impl Display for MyStruct { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} + +fn main() { + let instance = MyStruct; + panic!("this is wrong: {}", instance); +} diff --git a/tests/ui/panics/panic-in-message-fmt.run.stderr b/tests/ui/panics/panic-in-message-fmt.run.stderr new file mode 100644 index 00000000000..c3a5733c8ae --- /dev/null +++ b/tests/ui/panics/panic-in-message-fmt.run.stderr @@ -0,0 +1,2 @@ +panicked at $DIR/panic-in-message-fmt.rs:18:9: +thread panicked while processing panic. aborting. diff --git a/tests/ui/parser/issues/issue-64732.rs b/tests/ui/parser/issues/issue-64732.rs index 2db51ea6042..ff0f97ea211 100644 --- a/tests/ui/parser/issues/issue-64732.rs +++ b/tests/ui/parser/issues/issue-64732.rs @@ -5,5 +5,5 @@ fn main() { //~| HELP if you meant to write a byte string literal, use double quotes let _bar = 'hello'; //~^ ERROR character literal may only contain one codepoint - //~| HELP if you meant to write a `str` literal, use double quotes + //~| HELP if you meant to write a string literal, use double quotes } diff --git a/tests/ui/parser/issues/issue-64732.stderr b/tests/ui/parser/issues/issue-64732.stderr index 80462549377..7ec2df6d3bf 100644 --- a/tests/ui/parser/issues/issue-64732.stderr +++ b/tests/ui/parser/issues/issue-64732.stderr @@ -7,7 +7,7 @@ LL | let _foo = b'hello\0'; help: if you meant to write a byte string literal, use double quotes | LL | let _foo = b"hello\0"; - | ~~~~~~~~~~ + | ~~ ~ error: character literal may only contain one codepoint --> $DIR/issue-64732.rs:6:16 @@ -15,10 +15,10 @@ error: character literal may only contain one codepoint LL | let _bar = 'hello'; | ^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _bar = "hello"; - | ~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/unicode-character-literal.fixed b/tests/ui/parser/unicode-character-literal.fixed index 9e31890151c..e401ecaf5da 100644 --- a/tests/ui/parser/unicode-character-literal.fixed +++ b/tests/ui/parser/unicode-character-literal.fixed @@ -7,12 +7,12 @@ fn main() { let _spade = "♠️"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = "ṩ̂̊"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.rs b/tests/ui/parser/unicode-character-literal.rs index d886e5b26a5..428e4e1ac5a 100644 --- a/tests/ui/parser/unicode-character-literal.rs +++ b/tests/ui/parser/unicode-character-literal.rs @@ -7,12 +7,12 @@ fn main() { let _spade = '♠️'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = 'ṩ̂̊'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.stderr b/tests/ui/parser/unicode-character-literal.stderr index 5cd3bd0fe69..726cde2b413 100644 --- a/tests/ui/parser/unicode-character-literal.stderr +++ b/tests/ui/parser/unicode-character-literal.stderr @@ -9,10 +9,10 @@ note: this `♠` is followed by the combining mark `\u{fe0f}` | LL | let _spade = '♠️'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _spade = "♠️"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:12:14 @@ -25,10 +25,10 @@ note: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` | LL | let _s = 'ṩ̂̊'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _s = "ṩ̂̊"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:17:14 diff --git a/tests/ui/polymorphization/ice-poly-with-mir-opts-90192.rs b/tests/ui/polymorphization/ice-poly-with-mir-opts-90192.rs new file mode 100644 index 00000000000..4557c7e517c --- /dev/null +++ b/tests/ui/polymorphization/ice-poly-with-mir-opts-90192.rs @@ -0,0 +1,20 @@ +// issue: rust-lang/rust#90192 +// ICE assertion failed: matches!(ty.kind(), ty :: Param(_)) +//@ compile-flags:-Zpolymorphize=on -Zmir-opt-level=3 +//@ build-pass + +fn test<T>() { + std::mem::size_of::<T>(); +} + +pub fn foo<T>(_: T) -> &'static fn() { + &(test::<T> as fn()) +} + +fn outer<T>() { + foo(|| ()); +} + +fn main() { + outer::<u8>(); +} diff --git a/tests/ui/print_type_sizes/async.stdout b/tests/ui/print_type_sizes/async.stdout index e1be98f85d8..1df4d85d09e 100644 --- a/tests/ui/print_type_sizes/async.stdout +++ b/tests/ui/print_type_sizes/async.stdout @@ -5,7 +5,7 @@ print-type-size upvar `.arg`: 8192 bytes print-type-size variant `Suspend0`: 16385 bytes print-type-size upvar `.arg`: 8192 bytes print-type-size local `.arg`: 8192 bytes -print-type-size local `.__awaitee`: 1 bytes +print-type-size local `.__awaitee`: 1 bytes, type: {async fn body@$DIR/async.rs:8:17: 8:19} print-type-size variant `Returned`: 8192 bytes print-type-size upvar `.arg`: 8192 bytes print-type-size variant `Panicked`: 8192 bytes diff --git a/tests/ui/privacy/decl-macro-infinite-global-import-cycle-ice-64784.rs b/tests/ui/privacy/decl-macro-infinite-global-import-cycle-ice-64784.rs new file mode 100644 index 00000000000..72b1ea7ccc8 --- /dev/null +++ b/tests/ui/privacy/decl-macro-infinite-global-import-cycle-ice-64784.rs @@ -0,0 +1,16 @@ +// ICE #64784 already borrowed: BorrowMutError +//@ check-pass +// issue: rust-lang/rust#64784 +#![feature(decl_macro)] + +pub macro m($i:ident, $j:ident) { + mod $i { + pub use crate::$j::*; + pub struct A; + } +} + +m!(x, y); +m!(y, x); + +fn main() {} diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/bar.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/bar.rs new file mode 100644 index 00000000000..58297c92693 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/bar.rs @@ -0,0 +1,6 @@ +//@ aux-crate:priv:foo=foo.rs +//@ compile-flags: -Zunstable-options + +#![crate_type = "rlib"] +extern crate foo; +pub struct Bar(pub i32); diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/foo.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/foo.rs new file mode 100644 index 00000000000..6fd950619e6 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/auxiliary/foo.rs @@ -0,0 +1,2 @@ +#![crate_type = "rlib"] +pub struct Foo(pub i32); diff --git a/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs b/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs new file mode 100644 index 00000000000..d7ade7f0e96 --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/priv-dep-issue-122756.rs @@ -0,0 +1,12 @@ +//@ aux-build: bar.rs +//@ aux-build: foo.rs +//@ build-pass + +#![deny(exported_private_dependencies)] + +// Ensure the libbar.rlib is loaded first. If the command line parameter `--extern foo` does not +// exist, previus version would fail to compile +#![crate_type = "rlib"] +extern crate bar; +extern crate foo; +pub fn baz() -> (Option<foo::Foo>, Option<bar::Bar>) { (None, None) } diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs new file mode 100644 index 00000000000..87ce4f1e14d --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs @@ -0,0 +1,15 @@ +// issue: rust-lang/rust#105047 +// ICE raw ptr comparison should already be caught in the trait systems + +#![feature(raw_ref_op)] + +const RCZ: *const i32 = &raw const *&0; + +const fn f() { + if let RCZ = &raw const *&0 { } + //~^ WARN function pointers and raw pointers not derived from integers in patterns + //~| ERROR pointers cannot be reliably compared during const eval + //~| WARN this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr new file mode 100644 index 00000000000..9c472cda244 --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr @@ -0,0 +1,31 @@ +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 <https://github.com/rust-lang/rust/issues/120362> + = note: `#[warn(pointer_structural_match)]` on by default + +error: pointers cannot be reliably compared during const eval + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = note: see issue #53020 <https://github.com/rust-lang/rust/issues/53020> for more information + +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 <https://github.com/rust-lang/rust/issues/120362> + = note: `#[warn(pointer_structural_match)]` on by default + diff --git a/tests/ui/sanitizer/cfi-self-ref.rs b/tests/ui/sanitizer/cfi-self-ref.rs new file mode 100644 index 00000000000..32d0b100702 --- /dev/null +++ b/tests/ui/sanitizer/cfi-self-ref.rs @@ -0,0 +1,33 @@ +// Check that encoding self-referential types works with #[repr(transparent)] + +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +//@ run-pass + +use std::marker::PhantomData; + +struct X<T> { + _x: u8, + p: PhantomData<T>, +} + +#[repr(transparent)] +struct Y(X<Y>); + +trait Fooable { + fn foo(&self, y: Y); +} + +struct Bar; + +impl Fooable for Bar { + fn foo(&self, _: Y) {} +} + +fn main() { + let x = &Bar as &dyn Fooable; + x.foo(Y(X {_x: 0, p: PhantomData})); +} diff --git a/tests/ui/sanitizer/cfi-virtual-auto.rs b/tests/ui/sanitizer/cfi-virtual-auto.rs new file mode 100644 index 00000000000..7a0c246a412 --- /dev/null +++ b/tests/ui/sanitizer/cfi-virtual-auto.rs @@ -0,0 +1,22 @@ +// Tests that calling a trait object method on a trait object with additional auto traits works. + +//@ needs-sanitizer-cfi +// FIXME(#122848) Remove only-linux once OSX CFI binaries work +//@ only-linux +//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi +//@ compile-flags: -C target-feature=-crt-static -C codegen-units=1 -C opt-level=0 +//@ run-pass + +trait Foo { + fn foo(&self); +} + +struct Bar; +impl Foo for Bar { + fn foo(&self) {} +} + +pub fn main() { + let x: &(dyn Foo + Send) = &Bar; + x.foo(); +} diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr new file mode 100644 index 00000000000..3f8011d961a --- /dev/null +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.32bit.stderr @@ -0,0 +1,25 @@ +error[E0391]: cycle detected when computing layout of `Foo` + | + = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... + = note: ...which again requires computing layout of `Foo`, completing the cycle +note: cycle used when const-evaluating + checking `_` + --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0080]: it is undefined behavior to use this value + --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation + | + = note: the raw bytes of the constant (size: 4, align: 4) { + 00 00 00 00 │ .... + } + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0391. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr new file mode 100644 index 00000000000..04e2c4483bf --- /dev/null +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.64bit.stderr @@ -0,0 +1,25 @@ +error[E0391]: cycle detected when computing layout of `Foo` + | + = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... + = note: ...which again requires computing layout of `Foo`, completing the cycle +note: cycle used when const-evaluating + checking `_` + --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0080]: it is undefined behavior to use this value + --> $DIR/stack-overflow-trait-infer-98842.rs:15:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation + | + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0391. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.rs b/tests/ui/sized/stack-overflow-trait-infer-98842.rs new file mode 100644 index 00000000000..54d50346cc8 --- /dev/null +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.rs @@ -0,0 +1,18 @@ +// #98842 stack overflow in trait inference +// issue: rust-lang/rust#98842 +//@ check-fail +//@ edition:2021 +//@ stderr-per-bitwidth +//@ ignore-endian-big +//~^^^^^^ ERROR cycle detected when computing layout of `Foo` + +// If the inner `Foo` is named through an associated type, +// the "infinite size" error does not occur. +struct Foo(<&'static Foo as ::core::ops::Deref>::Target); +// But Rust will be unable to know whether `Foo` is sized or not, +// and it will infinitely recurse somewhere trying to figure out the +// size of this pointer (is my guess): +const _: *const Foo = 0 as _; +//~^ ERROR it is undefined behavior to use this value + +pub fn main() {} diff --git a/tests/ui/sized/stack-overflow-trait-infer-98842.stderr b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr new file mode 100644 index 00000000000..8ddddeb5bf2 --- /dev/null +++ b/tests/ui/sized/stack-overflow-trait-infer-98842.stderr @@ -0,0 +1,25 @@ +error[E0391]: cycle detected when computing layout of `Foo` + | + = note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... + = note: ...which again requires computing layout of `Foo`, completing the cycle +note: cycle used when const-evaluating + checking `_` + --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0080]: it is undefined behavior to use this value + --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 + | +LL | const _: *const Foo = 0 as _; + | ^^^^^^^^^^^^^^^^^^^ a cycle occurred during layout computation + | + = note: the raw bytes of the constant (size: 8, align: 8) { + 00 00 00 00 00 00 00 00 │ ........ + } + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0080, E0391. +For more information about an error, try `rustc --explain E0080`. diff --git a/tests/ui/specialization/broken-mir-drop-glue-107228.rs b/tests/ui/specialization/broken-mir-drop-glue-107228.rs new file mode 100644 index 00000000000..5a6dbf9ffc7 --- /dev/null +++ b/tests/ui/specialization/broken-mir-drop-glue-107228.rs @@ -0,0 +1,28 @@ +// issue: rust-lang/rust#107228 +// ICE broken MIR in DropGlue +//@ compile-flags: -Zvalidate-mir +//@ check-pass + +#![feature(specialization)] +#![crate_type="lib"] +#![allow(incomplete_features)] + +pub(crate) trait SpecTrait { + type Assoc; +} + +impl<C> SpecTrait for C { + default type Assoc = Vec<Self>; +} + +pub(crate) struct AssocWrap<C: SpecTrait> { + _assoc: C::Assoc, +} + +fn instantiate<C: SpecTrait>() -> AssocWrap<C> { + loop {} +} + +pub fn main() { + instantiate::<()>(); +} diff --git a/tests/ui/str/str-as-char.stderr b/tests/ui/str/str-as-char.stderr index 44ec079e929..0638d371c17 100644 --- a/tests/ui/str/str-as-char.stderr +++ b/tests/ui/str/str-as-char.stderr @@ -4,10 +4,10 @@ error: character literal may only contain one codepoint LL | println!('●●'); | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!("●●"); - | ~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs new file mode 100644 index 00000000000..57f814bc81e --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Znext-solver + +trait Tr<'a> {} + +// Fulfillment in the new solver relies on an invariant to hold: Either +// `has_changed` is true, or computing a goal's certainty is idempotent. +// This isn't true for `ReError`, which we used to pass through in the +// canonicalizer even on input mode, which can cause a goal to go from +// ambig => pass, but we don't consider `has_changed` when the response +// only contains region constraints (since we usually uniquify regions). +// +// In this test: +// Implicit negative coherence tries to prove `W<?0>: Constrain<'?1>`, +// which will then match with the impl below. This constrains `'?1` to +// `ReError`, but still bails w/ ambiguity bc we can't prove `?0: Sized`. +// Then, when we recompute the goal `W<?0>: Constrain<'error>`, when +// collecting ambiguities and overflows, we end up assembling a default +// error candidate w/o ambiguity, which causes the goal to pass, and ICE. +impl<'a, A: ?Sized> Tr<'a> for W<A> {} +struct W<A: ?Sized>(A); +impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} +//~^ ERROR conflicting implementations of trait `Tr<'_>` for type `W<_>` + +trait Constrain<'a> {} +impl<A: Sized> Constrain<'missing> for W<A> {} +//~^ ERROR use of undeclared lifetime name `'missing` + +fn main() {} diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr new file mode 100644 index 00000000000..cf85c52fb42 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr @@ -0,0 +1,21 @@ +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/dont-canonicalize-re-error.rs:25:26 + | +LL | impl<A: Sized> Constrain<'missing> for W<A> {} + | - ^^^^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'missing` here: `'missing,` + +error[E0119]: conflicting implementations of trait `Tr<'_>` for type `W<_>` + --> $DIR/dont-canonicalize-re-error.rs:21:1 + | +LL | impl<'a, A: ?Sized> Tr<'a> for W<A> {} + | ----------------------------------- first implementation here +LL | struct W<A: ?Sized>(A); +LL | impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<_>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0261. +For more information about an error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs new file mode 100644 index 00000000000..3af299e5b11 --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs @@ -0,0 +1,15 @@ +trait FromResidual<R = <Self as Try>::Residual> { + fn from_residual(residual: R) -> Self; +} + +trait Try { + type Residual; +} + +fn w<'a, T: 'a, F: Fn(&'a T)>() { + let b: &dyn FromResidual = &(); + //~^ ERROR: the trait `FromResidual` cannot be made into an object + //~| ERROR: the trait `FromResidual` cannot be made into an object +} + +fn main() {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr new file mode 100644 index 00000000000..d5e9b1be63b --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -0,0 +1,33 @@ +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:17 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^ + | + = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause + +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:12 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^^^^^^ `FromResidual` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 + | +LL | trait FromResidual<R = <Self as Try>::Residual> { + | ------------ this trait cannot be made into an object... +LL | fn from_residual(residual: R) -> Self; + | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter +help: consider turning `from_residual` into a method by giving it a `&self` argument + | +LL | fn from_residual(&self, residual: R) -> Self; + | ++++++ +help: alternatively, consider constraining `from_residual` so it does not apply to trait objects + | +LL | fn from_residual(residual: R) -> Self where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs new file mode 100644 index 00000000000..023991c29d0 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs @@ -0,0 +1,36 @@ +// issue: rust-lang/rust#99945 +// ICE Failed to normalize + +#![feature(type_alias_impl_trait)] + +trait Widget<E> { + type State; + + fn make_state(&self) -> Self::State; +} + +impl<E> Widget<E> for () { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +struct StatefulWidget<F>(F); + +type StateWidget<'a> = impl Widget<&'a ()>; + +impl<F: for<'a> Fn(&'a ()) -> StateWidget<'a>> Widget<()> for StatefulWidget<F> { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +fn new_stateful_widget<F: for<'a> Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { + StatefulWidget(build) + //~^ ERROR expected generic lifetime parameter, found `'a` +} + +fn main() { + new_stateful_widget(|_| ()).make_state(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr new file mode 100644 index 00000000000..0c76feae198 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/failed-to-normalize-ice-99945.rs:34:29 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | ------------------- the expected opaque type +... +LL | new_stateful_widget(|_| ()).make_state(); + | ^^ expected opaque type, found `()` + | + = note: expected opaque type `StateWidget<'_>` + found unit type `()` + +error[E0792]: expected generic lifetime parameter, found `'a` + --> $DIR/failed-to-normalize-ice-99945.rs:29:5 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | StatefulWidget(build) + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0792. +For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs b/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs new file mode 100644 index 00000000000..be743e8e270 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/tait-param-inference-issue-117310.rs @@ -0,0 +1,28 @@ +//@ check-pass + +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +use std::ops::Deref; + +trait Trait {} +impl<A, B> Trait for (A, B, u8) where A: Deref, B: Deref<Target = A::Target>, {} +impl<A, B> Trait for (A, B, i8) {} + +type TaitSized = impl Sized; +fn def_tait1() -> TaitSized {} + +type TaitCopy = impl Copy; +fn def_tait2() -> TaitCopy {} + +fn impl_trait<T: Trait> () {} + +fn test() { + impl_trait::<(&TaitSized, &TaitCopy, _)>(); + impl_trait::<(&TaitCopy, &TaitSized, _)>(); + + impl_trait::<(&TaitCopy, &String, _)>(); + impl_trait::<(&TaitSized, &String, _)>(); +} + +fn main() {} diff --git a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs new file mode 100644 index 00000000000..ec475673d0d --- /dev/null +++ b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.rs @@ -0,0 +1,21 @@ +// ICE size_and_align_of::<[closure@test.rs:15:5: 17:7]> not supported #88212 +// issue: rust-lang/rust#88212 +#![feature(unsized_locals)] +//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + +trait Example {} +struct Foo(); + +impl Example for Foo {} + +fn example() -> Box<dyn Example> { + Box::new(Foo()) +} + +fn main() { + let x: dyn Example = *example(); + (move || { + let _y = x; + //~^ ERROR the size for values of type `dyn Example` cannot be known at compilation time + })(); +} diff --git a/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr new file mode 100644 index 00000000000..a0253ac1f35 --- /dev/null +++ b/tests/ui/unsized-locals/ice-size_and_align_of-closure-not-supported-88212.stderr @@ -0,0 +1,23 @@ +warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:3:12 + | +LL | #![feature(unsized_locals)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `dyn Example` cannot be known at compilation time + --> $DIR/ice-size_and_align_of-closure-not-supported-88212.rs:18:18 + | +LL | (move || { + | -- this closure captures all values by move +LL | let _y = x; + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Example` + = note: all values captured by value by a closure must have a statically known size + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs index 107cfbb9ff6..db155f4fa3c 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.rs @@ -1,11 +1,30 @@ trait Trait<const N: Trait = bar> { -//~^ ERROR cannot find value `bar` in this scope -//~| ERROR cycle detected when computing type of `Trait::N` -//~| ERROR cycle detected when computing type of `Trait::N` -//~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter -//~| WARN trait objects without an explicit `dyn` are deprecated -//~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! - fn fnc(&self) { + //~^ ERROR cannot find value `bar` in this scope + //~| ERROR cycle detected when computing type of `Trait::N` + //~| ERROR cycle detected when computing type of `Trait::N` + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + fn fnc<const N: Trait = u32>(&self) -> Trait { + //~^ ERROR the name `N` is already used for a generic parameter in this item's generic parameters + //~| ERROR expected value, found builtin type `u32` + //~| ERROR defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + //~| ERROR associated item referring to unboxed trait object for its own trait + //~| ERROR the trait `Trait` cannot be made into an object + //~| ERROR `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + //~| WARN trait objects without an explicit `dyn` are deprecated [bare_trait_objects] + //~| WARN this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + bar + //~^ ERROR cannot find value `bar` in this scope } } diff --git a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr index 2d5a0ede962..cf985d9d1fd 100644 --- a/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr +++ b/tests/ui/wf/ice-hir-wf-check-anon-const-issue-122199.stderr @@ -1,9 +1,30 @@ +error[E0403]: the name `N` is already used for a generic parameter in this item's generic parameters + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:18 + | +LL | trait Trait<const N: Trait = bar> { + | - first use of `N` +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^ already used + error[E0425]: cannot find value `bar` in this scope --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:30 | LL | trait Trait<const N: Trait = bar> { | ^^^ not found in this scope +error[E0423]: expected value, found builtin type `u32` + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:29 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ not a value + +error[E0425]: cannot find value `bar` in this scope + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:26:9 + | +LL | bar + | ^^^ not found in this scope + warning: trait objects without an explicit `dyn` are deprecated --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 | @@ -46,6 +67,84 @@ LL | trait Trait<const N: Trait = bar> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information +error: defaults for const parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:12 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^^^^^^^^^^^^^^^^ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: dyn Trait = u32>(&self) -> Trait { + | +++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: Trait = u32>(&self) -> dyn Trait { + | +++ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is an object-safe trait, use `dyn` + | +LL | trait Trait<const N: dyn Trait = bar> { + | +++ + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:22 | @@ -54,7 +153,76 @@ LL | trait Trait<const N: Trait = bar> { | = note: the only supported types are integers, `bool` and `char` -error: aborting due to 4 previous errors; 1 warning emitted +error: associated item referring to unboxed trait object for its own trait + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:44 + | +LL | trait Trait<const N: Trait = bar> { + | ----- in this trait +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | +help: you might have meant to use `Self` to refer to the implementing type + | +LL | fn fnc<const N: Trait = u32>(&self) -> Self { + | ~~~~ + +warning: trait objects without an explicit `dyn` are deprecated + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = warning: this is accepted in the current edition (Rust 2015) but is a hard error in Rust 2021! + = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html> + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: if this is an object-safe trait, use `dyn` + | +LL | fn fnc<const N: dyn Trait = u32>(&self) -> Trait { + | +++ + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + +error[E0038]: the trait `Trait` cannot be made into an object + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:1:13 + | +LL | trait Trait<const N: Trait = bar> { + | ^^^^^^^^^^^^^^^^^^^^ `Trait` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety> + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:8 + | +LL | trait Trait<const N: Trait = bar> { + | ----- this trait cannot be made into an object... +... +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^ ...because method `fnc` has generic type parameters + = help: consider moving `fnc` to another trait + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: `(dyn Trait<{const error}> + 'static)` is forbidden as the type of a const generic parameter + --> $DIR/ice-hir-wf-check-anon-const-issue-122199.rs:13:21 + | +LL | fn fnc<const N: Trait = u32>(&self) -> Trait { + | ^^^^^ + | + = note: the only supported types are integers, `bool` and `char` + +error: aborting due to 14 previous errors; 5 warnings emitted -Some errors have detailed explanations: E0391, E0425. -For more information about an error, try `rustc --explain E0391`. +Some errors have detailed explanations: E0038, E0391, E0403, E0423, E0425. +For more information about an error, try `rustc --explain E0038`. |
