diff --git a/compiler/rustc_ast_lowering/src/index.rs b/compiler/rustc_ast_lowering/src/index.rs index 11aa6b250b12..793fe9cfd5e2 100644 --- a/compiler/rustc_ast_lowering/src/index.rs +++ b/compiler/rustc_ast_lowering/src/index.rs @@ -55,6 +55,7 @@ pub(super) fn index_hir<'hir>( OwnerNode::TraitItem(item) => collector.visit_trait_item(item), OwnerNode::ImplItem(item) => collector.visit_impl_item(item), OwnerNode::ForeignItem(item) => collector.visit_foreign_item(item), + OwnerNode::AssocOpaqueTy(..) => unreachable!(), }; for (local_id, node) in collector.nodes.iter_enumerated() { diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e8ff64a7fd2c..6a7ee936f66d 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -642,23 +642,8 @@ fn make_owner_info(&mut self, node: hir::OwnerNode<'hir>) -> &'hir hir::OwnerInf let bodies = SortedMap::from_presorted_elements(bodies); // Don't hash unless necessary, because it's expensive. - let (opt_hash_including_bodies, attrs_hash) = if self.tcx.needs_crate_hash() { - self.tcx.with_stable_hashing_context(|mut hcx| { - let mut stable_hasher = StableHasher::new(); - node.hash_stable(&mut hcx, &mut stable_hasher); - // Bodies are stored out of line, so we need to pull them explicitly in the hash. - bodies.hash_stable(&mut hcx, &mut stable_hasher); - let h1 = stable_hasher.finish(); - - let mut stable_hasher = StableHasher::new(); - attrs.hash_stable(&mut hcx, &mut stable_hasher); - let h2 = stable_hasher.finish(); - - (Some(h1), Some(h2)) - }) - } else { - (None, None) - }; + let (opt_hash_including_bodies, attrs_hash) = + self.tcx.hash_owner_nodes(node, &bodies, &attrs); let num_nodes = self.item_local_id_counter.as_usize(); let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes); let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies }; diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs index 18c5960ffc68..cec479218b71 100644 --- a/compiler/rustc_codegen_cranelift/src/constant.rs +++ b/compiler/rustc_codegen_cranelift/src/constant.rs @@ -71,7 +71,7 @@ pub(crate) fn eval_mir_constant<'tcx>( // This cannot fail because we checked all required_consts in advance. let val = cv .eval(fx.tcx, ty::ParamEnv::reveal_all(), Some(constant.span)) - .expect("erroneous constant not captured by required_consts"); + .expect("erroneous constant missed by mono item collection"); (val, cv.ty()) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index 147939d3a529..e5f5146fac8f 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -203,57 +203,63 @@ fn store( val: &'ll Value, dst: PlaceRef<'tcx, &'ll Value>, ) { - if self.is_ignore() { - return; - } - if self.is_sized_indirect() { - OperandValue::Ref(val, None, self.layout.align.abi).store(bx, dst) - } else if self.is_unsized_indirect() { - bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); - } else if let PassMode::Cast { cast, pad_i32: _ } = &self.mode { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - bx.store(val, dst.llval, self.layout.align.abi); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(bx); - let scratch_align = cast.align(bx); - let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ... where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ... and then memcpy it to the intended destination. - bx.memcpy( - dst.llval, - self.layout.align.abi, - llscratch, - scratch_align, - bx.const_usize(self.layout.size.bytes()), - MemFlags::empty(), - ); - - bx.lifetime_end(llscratch, scratch_size); + match &self.mode { + PassMode::Ignore => {} + // Sized indirect arguments + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + let align = attrs.pointee_align.unwrap_or(self.layout.align.abi); + OperandValue::Ref(val, None, align).store(bx, dst); + } + // Unsized indirect qrguments + PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + bug!("unsized `ArgAbi` must be handled through `store_fn_arg`"); + } + PassMode::Cast { cast, pad_i32: _ } => { + // FIXME(eddyb): Figure out when the simpler Store is safe, clang + // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. + let can_store_through_cast_ptr = false; + if can_store_through_cast_ptr { + bx.store(val, dst.llval, self.layout.align.abi); + } else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + + // We instead thus allocate some scratch space... + let scratch_size = cast.size(bx); + let scratch_align = cast.align(bx); + let llscratch = bx.alloca(cast.llvm_type(bx), scratch_align); + bx.lifetime_start(llscratch, scratch_size); + + // ... where we first store the value... + bx.store(val, llscratch, scratch_align); + + // ... and then memcpy it to the intended destination. + bx.memcpy( + dst.llval, + self.layout.align.abi, + llscratch, + scratch_align, + bx.const_usize(self.layout.size.bytes()), + MemFlags::empty(), + ); + + bx.lifetime_end(llscratch, scratch_size); + } + } + _ => { + OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); } - } else { - OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst); } } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs index 017843c7e7d1..2af28146a51e 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs @@ -164,6 +164,15 @@ pub(crate) fn from_mapping( end_line, end_col, ), + MappingKind::Branch { true_term, false_term } => Self::branch_region( + Counter::from_term(true_term), + Counter::from_term(false_term), + local_file_id, + start_line, + start_col, + end_line, + end_col, + ), } } @@ -188,9 +197,6 @@ pub(crate) fn code_region( } } - // This function might be used in the future; the LLVM API is still evolving, as is coverage - // support. - #[allow(dead_code)] pub(crate) fn branch_region( counter: Counter, false_counter: Counter, diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index 733a77d24c2a..133084b7c12a 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -88,7 +88,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) { match coverage.kind { // Marker statements have no effect during codegen, // so return early and don't create `func_coverage`. - CoverageKind::SpanMarker => return, + CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => return, // Match exhaustively to ensure that newly-added kinds are classified correctly. CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } => {} } @@ -108,7 +108,7 @@ fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) { let Coverage { kind } = coverage; match *kind { - CoverageKind::SpanMarker => unreachable!( + CoverageKind::SpanMarker | CoverageKind::BlockMarker { .. } => unreachable!( "unexpected marker statement {kind:?} should have caused an early return" ), CoverageKind::CounterIncrement { id } => { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index e70cc9b62161..1406a7d9b5c2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1081,6 +1081,21 @@ fn is_illegal_instruction(_status: &ExitStatus) -> bool { } } + if sess.target.is_like_aix { + let stripcmd = "/usr/bin/strip"; + match strip { + Strip::Debuginfo => { + // FIXME: AIX's strip utility only offers option to strip line number information. + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-l")) + } + Strip::Symbols => { + // Must be noted this option might remove symbol __aix_rust_metadata and thus removes .info section which contains metadata. + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-r")) + } + Strip::None => {} + } + } + Ok(()) } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index b4e054417f31..bb23e21b2a23 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1640,16 +1640,7 @@ fn control_flow_guard(&mut self) {} fn ehcont_guard(&mut self) {} - fn debuginfo(&mut self, strip: Strip, _: &[PathBuf]) { - match strip { - Strip::None => {} - // FIXME: -s strips the symbol table, line number information - // and relocation information. - Strip::Debuginfo | Strip::Symbols => { - self.cmd.arg("-s"); - } - } - } + fn debuginfo(&mut self, _: Strip, _: &[PathBuf]) {} fn no_crt_objects(&mut self) {} diff --git a/compiler/rustc_codegen_ssa/src/mir/constant.rs b/compiler/rustc_codegen_ssa/src/mir/constant.rs index d532bd904267..ff899c50b3de 100644 --- a/compiler/rustc_codegen_ssa/src/mir/constant.rs +++ b/compiler/rustc_codegen_ssa/src/mir/constant.rs @@ -21,11 +21,11 @@ pub fn eval_mir_constant_to_operand( } pub fn eval_mir_constant(&self, constant: &mir::ConstOperand<'tcx>) -> mir::ConstValue<'tcx> { - // `MirUsedCollector` visited all constants before codegen began, so if we got here there - // can be no more constants that fail to evaluate. + // `MirUsedCollector` visited all required_consts before codegen began, so if we got here + // there can be no more constants that fail to evaluate. self.monomorphize(constant.const_) .eval(self.cx.tcx(), ty::ParamEnv::reveal_all(), Some(constant.span)) - .expect("erroneous constant not captured by required_consts") + .expect("erroneous constant missed by mono item collection") } /// This is a convenience helper for `simd_shuffle_indices`. It has the precondition diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index bac10f313366..387a5366b209 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -211,7 +211,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // It may seem like we should iterate over `required_consts` to ensure they all successfully // evaluate; however, the `MirUsedCollector` already did that during the collection phase of - // monomorphization so we don't have to do it again. + // monomorphization, and if there is an error during collection then codegen never starts -- so + // we don't have to do it again. fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); @@ -376,29 +377,45 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } } - if arg.is_sized_indirect() { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up. - // FIXME: lifetimes - let llarg = bx.get_param(llarg_idx); - llarg_idx += 1; - LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) - } else if arg.is_unsized_indirect() { - // As the storage for the indirect argument lives during - // the whole function call, we just copy the fat pointer. - let llarg = bx.get_param(llarg_idx); - llarg_idx += 1; - let llextra = bx.get_param(llarg_idx); - llarg_idx += 1; - let indirect_operand = OperandValue::Pair(llarg, llextra); + match arg.mode { + // Sized indirect arguments + PassMode::Indirect { attrs, meta_attrs: None, on_stack: _ } => { + // Don't copy an indirect argument to an alloca, the caller already put it + // in a temporary alloca and gave it up. + // FIXME: lifetimes + if let Some(pointee_align) = attrs.pointee_align + && pointee_align < arg.layout.align.abi + { + // ...unless the argument is underaligned, then we need to copy it to + // a higher-aligned alloca. + let tmp = PlaceRef::alloca(bx, arg.layout); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) + } else { + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + LocalRef::Place(PlaceRef::new_sized(llarg, arg.layout)) + } + } + // Unsized indirect qrguments + PassMode::Indirect { attrs: _, meta_attrs: Some(_), on_stack: _ } => { + // As the storage for the indirect argument lives during + // the whole function call, we just copy the fat pointer. + let llarg = bx.get_param(llarg_idx); + llarg_idx += 1; + let llextra = bx.get_param(llarg_idx); + llarg_idx += 1; + let indirect_operand = OperandValue::Pair(llarg, llextra); - let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); - indirect_operand.store(bx, tmp); - LocalRef::UnsizedPlace(tmp) - } else { - let tmp = PlaceRef::alloca(bx, arg.layout); - bx.store_fn_arg(arg, &mut llarg_idx, tmp); - LocalRef::Place(tmp) + let tmp = PlaceRef::alloca_unsized_indirect(bx, arg.layout); + indirect_operand.store(bx, tmp); + LocalRef::UnsizedPlace(tmp) + } + _ => { + let tmp = PlaceRef::alloca(bx, arg.layout); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + LocalRef::Place(tmp) + } } }) .collect::>(); diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index f3af633b4e56..0046190d20cc 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -374,12 +374,6 @@ const_eval_unallowed_op_in_const_context = const_eval_unavailable_target_features_for_fn = calling a function that requires unavailable target features: {$unavailable_feats} -const_eval_undefined_behavior = - it is undefined behavior to use this value - -const_eval_undefined_behavior_note = - The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. - const_eval_uninhabited_enum_variant_read = read discriminant of an uninhabited enum variant const_eval_uninhabited_enum_variant_written = @@ -434,6 +428,12 @@ const_eval_validation_expected_raw_ptr = expected a raw pointer const_eval_validation_expected_ref = expected a reference const_eval_validation_expected_str = expected a string +const_eval_validation_failure = + it is undefined behavior to use this value + +const_eval_validation_failure_note = + The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior. + const_eval_validation_front_matter_invalid_value = constructing invalid value const_eval_validation_front_matter_invalid_value_with_path = constructing invalid value at {$path} diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs index b6adee435ba3..763344207c46 100644 --- a/compiler/rustc_const_eval/src/const_eval/error.rs +++ b/compiler/rustc_const_eval/src/const_eval/error.rs @@ -2,15 +2,16 @@ use rustc_errors::{DiagArgName, DiagArgValue, DiagMessage, Diagnostic, IntoDiagArg}; use rustc_hir::CRATE_HIR_ID; +use rustc_middle::mir::interpret::Provenance; use rustc_middle::mir::AssertKind; use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::{layout::LayoutError, ConstInt}; use rustc_span::{Span, Symbol, DUMMY_SP}; -use super::{CompileTimeInterpreter, InterpCx}; +use super::CompileTimeInterpreter; use crate::errors::{self, FrameNote, ReportErrorExt}; -use crate::interpret::{ErrorHandled, InterpError, InterpErrorInfo, MachineStopType}; +use crate::interpret::{ErrorHandled, Frame, InterpError, InterpErrorInfo, MachineStopType}; /// The CTFE machine has some custom error kinds. #[derive(Clone, Debug)] @@ -58,15 +59,12 @@ fn into(self) -> InterpErrorInfo<'tcx> { pub fn get_span_and_frames<'tcx, 'mir>( tcx: TyCtxtAt<'tcx>, - machine: &CompileTimeInterpreter<'mir, 'tcx>, + stack: &[Frame<'mir, 'tcx, impl Provenance, impl Sized>], ) -> (Span, Vec) where 'tcx: 'mir, { - let mut stacktrace = - InterpCx::>::generate_stacktrace_from_stack( - &machine.stack, - ); + let mut stacktrace = Frame::generate_stacktrace_from_stack(stack); // Filter out `requires_caller_location` frames. stacktrace.retain(|frame| !frame.instance.def.requires_caller_location(*tcx)); let span = stacktrace.first().map(|f| f.span).unwrap_or(tcx.span); @@ -170,7 +168,7 @@ pub(super) fn lint<'tcx, 'mir, L>( ) where L: for<'a> rustc_errors::LintDiagnostic<'a, ()>, { - let (span, frames) = get_span_and_frames(tcx, machine); + let (span, frames) = get_span_and_frames(tcx, &machine.stack); tcx.emit_node_span_lint( lint, 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 5f4408ebbc6c..5a1c7cc4209a 100644 --- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs +++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs @@ -18,18 +18,18 @@ use crate::errors::ConstEvalError; use crate::interpret::eval_nullary_intrinsic; use crate::interpret::{ - create_static_alloc, intern_const_alloc_recursive, take_static_root_alloc, CtfeValidationMode, - GlobalId, Immediate, InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, - OpTy, RefTracking, StackPopCleanup, + create_static_alloc, intern_const_alloc_recursive, CtfeValidationMode, GlobalId, Immediate, + InternKind, InterpCx, InterpError, InterpResult, MPlaceTy, MemoryKind, OpTy, RefTracking, + StackPopCleanup, }; // Returns a pointer to where the result lives -#[instrument(level = "trace", skip(ecx, body), ret)] -fn eval_body_using_ecx<'mir, 'tcx>( +#[instrument(level = "trace", skip(ecx, body))] +fn eval_body_using_ecx<'mir, 'tcx, R: InterpretationResult<'tcx>>( ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, cid: GlobalId<'tcx>, body: &'mir mir::Body<'tcx>, -) -> InterpResult<'tcx, MPlaceTy<'tcx>> { +) -> InterpResult<'tcx, R> { trace!(?ecx.param_env); let tcx = *ecx.tcx; assert!( @@ -84,7 +84,10 @@ fn eval_body_using_ecx<'mir, 'tcx>( // Intern the result intern_const_alloc_recursive(ecx, intern_kind, &ret)?; - Ok(ret) + // Since evaluation had no errors, validate the resulting constant. + const_validate_mplace(&ecx, &ret, cid)?; + + Ok(R::make_result(ret, ecx)) } /// The `InterpCx` is only meant to be used to do field and index projections into constants for @@ -282,18 +285,26 @@ pub fn eval_static_initializer_provider<'tcx>( let instance = ty::Instance::mono(tcx, def_id.to_def_id()); let cid = rustc_middle::mir::interpret::GlobalId { instance, promoted: None }; - let mut ecx = InterpCx::new( - tcx, - tcx.def_span(def_id), - ty::ParamEnv::reveal_all(), - // Statics (and promoteds inside statics) may access other statics, because unlike consts - // they do not have to behave "as if" they were evaluated at runtime. - CompileTimeInterpreter::new(CanAccessMutGlobal::Yes, CheckAlignment::Error), - ); - let alloc_id = eval_in_interpreter(&mut ecx, cid, true)?.alloc_id; - let alloc = take_static_root_alloc(&mut ecx, alloc_id); - let alloc = tcx.mk_const_alloc(alloc); - Ok(alloc) + eval_in_interpreter(tcx, cid, ty::ParamEnv::reveal_all()) +} + +pub trait InterpretationResult<'tcx> { + /// This function takes the place where the result of the evaluation is stored + /// and prepares it for returning it in the appropriate format needed by the specific + /// evaluation query. + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self; +} + +impl<'tcx> InterpretationResult<'tcx> for ConstAlloc<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + _ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + ConstAlloc { alloc_id: mplace.ptr().provenance.unwrap().alloc_id(), ty: mplace.layout.ty } + } } #[instrument(skip(tcx), level = "debug")] @@ -319,92 +330,64 @@ pub fn eval_to_allocation_raw_provider<'tcx>( trace!("const eval: {:?} ({})", key, instance); } - let cid = key.value; + eval_in_interpreter(tcx, key.value, key.param_env) +} + +fn eval_in_interpreter<'tcx, R: InterpretationResult<'tcx>>( + tcx: TyCtxt<'tcx>, + cid: GlobalId<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> Result { let def = cid.instance.def.def_id(); let is_static = tcx.is_static(def); let mut ecx = InterpCx::new( tcx, tcx.def_span(def), - key.param_env, + param_env, // Statics (and promoteds inside statics) may access mutable global memory, because unlike consts // they do not have to behave "as if" they were evaluated at runtime. // For consts however we want to ensure they behave "as if" they were evaluated at runtime, // so we have to reject reading mutable global memory. CompileTimeInterpreter::new(CanAccessMutGlobal::from(is_static), CheckAlignment::Error), ); - eval_in_interpreter(&mut ecx, cid, is_static) -} - -pub fn eval_in_interpreter<'mir, 'tcx>( - ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, - cid: GlobalId<'tcx>, - is_static: bool, -) -> ::rustc_middle::mir::interpret::EvalToAllocationRawResult<'tcx> { - // `is_static` just means "in static", it could still be a promoted! - debug_assert_eq!(is_static, ecx.tcx.static_mutability(cid.instance.def_id()).is_some()); - let res = ecx.load_mir(cid.instance.def, cid.promoted); - match res.and_then(|body| eval_body_using_ecx(ecx, cid, body)) { - Err(error) => { - let (error, backtrace) = error.into_parts(); - backtrace.print_backtrace(); + res.and_then(|body| eval_body_using_ecx(&mut ecx, cid, body)).map_err(|error| { + let (error, backtrace) = error.into_parts(); + backtrace.print_backtrace(); - let (kind, instance) = if is_static { - ("static", String::new()) + let (kind, instance) = if ecx.tcx.is_static(cid.instance.def_id()) { + ("static", String::new()) + } else { + // If the current item has generics, we'd like to enrich the message with the + // instance and its args: to show the actual compile-time values, in addition to + // the expression, leading to the const eval error. + let instance = &cid.instance; + if !instance.args.is_empty() { + let instance = with_no_trimmed_paths!(instance.to_string()); + ("const_with_path", instance) } else { - // If the current item has generics, we'd like to enrich the message with the - // instance and its args: to show the actual compile-time values, in addition to - // the expression, leading to the const eval error. - let instance = &cid.instance; - if !instance.args.is_empty() { - let instance = with_no_trimmed_paths!(instance.to_string()); - ("const_with_path", instance) - } else { - ("const", String::new()) - } - }; - - Err(super::report( - *ecx.tcx, - error, - None, - || super::get_span_and_frames(ecx.tcx, &ecx.machine), - |span, frames| ConstEvalError { - span, - error_kind: kind, - instance, - frame_notes: frames, - }, - )) - } - Ok(mplace) => { - // Since evaluation had no errors, validate the resulting constant. - - // Temporarily allow access to the static_root_ids for the purpose of validation. - let static_root_ids = ecx.machine.static_root_ids.take(); - let res = const_validate_mplace(&ecx, &mplace, cid); - ecx.machine.static_root_ids = static_root_ids; - - let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); - - // Validation failed, report an error. - if let Err(error) = res { - Err(const_report_error(&ecx, error, alloc_id)) - } else { - // Convert to raw constant - Ok(ConstAlloc { alloc_id, ty: mplace.layout.ty }) + ("const", String::new()) } - } - } + }; + + super::report( + *ecx.tcx, + error, + None, + || super::get_span_and_frames(ecx.tcx, ecx.stack()), + |span, frames| ConstEvalError { span, error_kind: kind, instance, frame_notes: frames }, + ) + }) } #[inline(always)] -pub fn const_validate_mplace<'mir, 'tcx>( +fn const_validate_mplace<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, mplace: &MPlaceTy<'tcx>, cid: GlobalId<'tcx>, -) -> InterpResult<'tcx> { +) -> Result<(), ErrorHandled> { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); let mut ref_tracking = RefTracking::new(mplace.clone()); let mut inner = false; while let Some((mplace, path)) = ref_tracking.todo.pop() { @@ -418,7 +401,10 @@ pub fn const_validate_mplace<'mir, 'tcx>( CtfeValidationMode::Const { allow_immutable_unsafe_cell: !inner } } }; - ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode)?; + ecx.const_validate_operand(&mplace.into(), path, &mut ref_tracking, mode) + // Instead of just reporting the `InterpError` via the usual machinery, we give a more targetted + // error about the validation failure. + .map_err(|error| report_validation_error(&ecx, error, alloc_id))?; inner = true; } @@ -426,7 +412,7 @@ pub fn const_validate_mplace<'mir, 'tcx>( } #[inline(always)] -pub fn const_report_error<'mir, 'tcx>( +fn report_validation_error<'mir, 'tcx>( ecx: &InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, error: InterpErrorInfo<'tcx>, alloc_id: AllocId, @@ -444,7 +430,7 @@ pub fn const_report_error<'mir, 'tcx>( *ecx.tcx, error, None, - || crate::const_eval::get_span_and_frames(ecx.tcx, &ecx.machine), - move |span, frames| errors::UndefinedBehavior { span, ub_note, frames, raw_bytes }, + || crate::const_eval::get_span_and_frames(ecx.tcx, ecx.stack()), + move |span, frames| errors::ValidationFailure { span, ub_note, frames, raw_bytes }, ) } diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs index 289dcb7d01d6..d0d6adbfad06 100644 --- a/compiler/rustc_const_eval/src/const_eval/mod.rs +++ b/compiler/rustc_const_eval/src/const_eval/mod.rs @@ -5,7 +5,7 @@ use rustc_middle::query::TyCtxtAt; use rustc_middle::ty::{self, Ty}; -use crate::interpret::{format_interp_error, InterpCx}; +use crate::interpret::format_interp_error; mod error; mod eval_queries; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 46790264359b..cc32640408b7 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -412,11 +412,11 @@ pub struct NullaryIntrinsicError { } #[derive(Diagnostic)] -#[diag(const_eval_undefined_behavior, code = E0080)] -pub struct UndefinedBehavior { +#[diag(const_eval_validation_failure, code = E0080)] +pub struct ValidationFailure { #[primary_span] pub span: Span, - #[note(const_eval_undefined_behavior_note)] + #[note(const_eval_validation_failure_note)] pub ub_note: Option<()>, #[subdiagnostic] pub frames: Vec, diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index a484fbd892c6..09e9cc4b35d3 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -220,9 +220,6 @@ pub(super) fn access(&self) -> InterpResult<'tcx, &Operand> { /// Overwrite the local. If the local can be overwritten in place, return a reference /// to do so; otherwise return the `MemPlace` to consult instead. - /// - /// Note: Before calling this, call the `before_access_local_mut` machine hook! You may be - /// invalidating machine invariants otherwise! #[inline(always)] pub(super) fn access_mut(&mut self) -> InterpResult<'tcx, &mut Operand> { match &mut self.value { @@ -279,6 +276,39 @@ pub fn lint_root(&self) -> Option { } }) } + + /// Returns the address of the buffer where the locals are stored. This is used by `Place` as a + /// sanity check to detect bugs where we mix up which stack frame a place refers to. + #[inline(always)] + pub(super) fn locals_addr(&self) -> usize { + self.locals.raw.as_ptr().addr() + } + + #[must_use] + pub fn generate_stacktrace_from_stack(stack: &[Self]) -> Vec> { + let mut frames = Vec::new(); + // This deliberately does *not* honor `requires_caller_location` since it is used for much + // more than just panics. + for frame in stack.iter().rev() { + let span = match frame.loc { + Left(loc) => { + // If the stacktrace passes through MIR-inlined source scopes, add them. + let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); + let mut scope_data = &frame.body.source_scopes[scope]; + while let Some((instance, call_span)) = scope_data.inlined { + frames.push(FrameInfo { span, instance }); + span = call_span; + scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; + } + span + } + Right(span) => span, + }; + frames.push(FrameInfo { span, instance: frame.instance }); + } + trace!("generate stacktrace: {:#?}", frames); + frames + } } // FIXME: only used by miri, should be removed once translatable. @@ -645,7 +675,7 @@ pub(crate) fn find_closest_untracked_caller_location(&self) -> Span { } #[inline(always)] - pub fn layout_of_local( + pub(super) fn layout_of_local( &self, frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, @@ -896,7 +926,7 @@ pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> // Copy return value. Must of course happen *before* we deallocate the locals. let copy_ret_result = if !unwinding { let op = self - .local_to_op(self.frame(), mir::RETURN_PLACE, None) + .local_to_op(mir::RETURN_PLACE, None) .expect("return place should always be live"); let dest = self.frame().return_place.clone(); let err = if self.stack().len() == 1 { @@ -1166,37 +1196,9 @@ pub fn dump_place( PlacePrinter { ecx: self, place: *place.place() } } - #[must_use] - pub fn generate_stacktrace_from_stack( - stack: &[Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>], - ) -> Vec> { - let mut frames = Vec::new(); - // This deliberately does *not* honor `requires_caller_location` since it is used for much - // more than just panics. - for frame in stack.iter().rev() { - let span = match frame.loc { - Left(loc) => { - // If the stacktrace passes through MIR-inlined source scopes, add them. - let mir::SourceInfo { mut span, scope } = *frame.body.source_info(loc); - let mut scope_data = &frame.body.source_scopes[scope]; - while let Some((instance, call_span)) = scope_data.inlined { - frames.push(FrameInfo { span, instance }); - span = call_span; - scope_data = &frame.body.source_scopes[scope_data.parent_scope.unwrap()]; - } - span - } - Right(span) => span, - }; - frames.push(FrameInfo { span, instance: frame.instance }); - } - trace!("generate stacktrace: {:#?}", frames); - frames - } - #[must_use] pub fn generate_stacktrace(&self) -> Vec> { - Self::generate_stacktrace_from_stack(self.stack()) + Frame::generate_stacktrace_from_stack(self.stack()) } } @@ -1212,18 +1214,16 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self.place { - Place::Local { frame, local, offset } => { + Place::Local { local, offset, locals_addr } => { + debug_assert_eq!(locals_addr, self.ecx.frame().locals_addr()); let mut allocs = Vec::new(); write!(fmt, "{local:?}")?; if let Some(offset) = offset { write!(fmt, "+{:#x}", offset.bytes())?; } - if frame != self.ecx.frame_idx() { - write!(fmt, " ({} frames up)", self.ecx.frame_idx() - frame)?; - } write!(fmt, ":")?; - match self.ecx.stack()[frame].locals[local].value { + match self.ecx.frame().locals[local].value { LocalValue::Dead => write!(fmt, " is dead")?, LocalValue::Live(Operand::Immediate(Immediate::Uninit)) => { write!(fmt, " is uninitialized")? diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index c30a13624178..17bb59aae8f1 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -176,7 +176,7 @@ pub fn intern_const_alloc_recursive< // This gives us the initial set of nested allocations, which will then all be processed // recursively in the loop below. let mut todo: Vec<_> = if is_static { - // Do not steal the root allocation, we need it later for `take_static_root_alloc` + // Do not steal the root allocation, we need it later to create the return value of `eval_static_initializer`. // But still change its mutability to match the requested one. let alloc = ecx.memory.alloc_map.get_mut(&base_alloc_id).unwrap(); alloc.1.mutability = base_mutability; diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index b260112c7830..afc4a80c283f 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -260,24 +260,6 @@ fn generate_nan, F2: Float>( F2::NAN } - /// Called before writing the specified `local` of the `frame`. - /// Since writing a ZST is not actually accessing memory or locals, this is never invoked - /// for ZST reads. - /// - /// Due to borrow checker trouble, we indicate the `frame` as an index rather than an `&mut - /// Frame`. - #[inline(always)] - fn before_access_local_mut<'a>( - _ecx: &'a mut InterpCx<'mir, 'tcx, Self>, - _frame: usize, - _local: mir::Local, - ) -> InterpResult<'tcx> - where - 'tcx: 'mir, - { - Ok(()) - } - /// Called before a basic block terminator is executed. #[inline] fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> { @@ -531,7 +513,6 @@ fn after_stack_pop( #[inline(always)] fn after_local_allocated( _ecx: &mut InterpCx<'mir, 'tcx, Self>, - _frame: usize, _local: mir::Local, _mplace: &MPlaceTy<'tcx, Self::Provenance>, ) -> InterpResult<'tcx> { diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs index 2ed879ca72b5..474d35b2aa3a 100644 --- a/compiler/rustc_const_eval/src/interpret/mod.rs +++ b/compiler/rustc_const_eval/src/interpret/mod.rs @@ -39,5 +39,5 @@ }; pub(crate) use self::intrinsics::eval_nullary_intrinsic; -pub(crate) use self::util::{create_static_alloc, take_static_root_alloc}; +pub(crate) use self::util::create_static_alloc; use eval_context::{from_known_layout, mir_assign_valid_types}; diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 317e5673b51b..e49067a2f006 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -13,9 +13,9 @@ use rustc_target::abi::{self, Abi, HasDataLayout, Size}; use super::{ - alloc_range, from_known_layout, mir_assign_valid_types, CtfeProvenance, Frame, InterpCx, - InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, - Projectable, Provenance, Scalar, + alloc_range, from_known_layout, mir_assign_valid_types, CtfeProvenance, InterpCx, InterpResult, + MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable, + Provenance, Scalar, }; /// An `Immediate` represents a single immediate self-contained Rust value. @@ -633,17 +633,17 @@ pub fn operand_to_simd( } } - /// Read from a local. + /// Read from a local of the current frame. /// Will not access memory, instead an indirect `Operand` is returned. /// /// This is public because it is used by [priroda](https://github.com/oli-obk/priroda) to get an /// OpTy from a local. pub fn local_to_op( &self, - frame: &Frame<'mir, 'tcx, M::Provenance, M::FrameExtra>, local: mir::Local, layout: Option>, ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { + let frame = self.frame(); let layout = self.layout_of_local(frame, local, layout)?; let op = *frame.locals[local].access()?; if matches!(op, Operand::Immediate(_)) { @@ -661,9 +661,10 @@ pub fn place_to_op( ) -> InterpResult<'tcx, OpTy<'tcx, M::Provenance>> { match place.as_mplace_or_local() { Left(mplace) => Ok(mplace.into()), - Right((frame, local, offset)) => { + Right((local, offset, locals_addr)) => { debug_assert!(place.layout.is_sized()); // only sized locals can ever be `Place::Local`. - let base = self.local_to_op(&self.stack()[frame], local, None)?; + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + let base = self.local_to_op(local, None)?; Ok(match offset { Some(offset) => base.offset(offset, place.layout, self)?, None => { @@ -687,7 +688,7 @@ pub fn eval_place_to_op( // here is not the entire place. let layout = if mir_place.projection.is_empty() { layout } else { None }; - let mut op = self.local_to_op(self.frame(), mir_place.local, layout)?; + let mut op = self.local_to_op(mir_place.local, layout)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { op = self.project(&op, elem)? diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 60f7710c11d7..1a2f1194f89a 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -187,11 +187,13 @@ pub(super) enum Place { /// where in the local this place is located; if it is `None`, no projection has been applied. /// Such projections are meaningful even if the offset is 0, since they can change layouts. /// (Without that optimization, we'd just always be a `MemPlace`.) - /// Note that this only stores the frame index, not the thread this frame belongs to -- that is - /// implicit. This means a `Place` must never be moved across interpreter thread boundaries! + /// `Local` places always refer to the current stack frame, so they are unstable under + /// function calls/returns and switching betweens stacks of different threads! + /// We carry around the address of the `locals` buffer of the correct stack frame as a sanity + /// chec to be able to catch some cases of using a dangling `Place`. /// /// This variant shall not be used for unsized types -- those must always live in memory. - Local { frame: usize, local: mir::Local, offset: Option }, + Local { local: mir::Local, offset: Option, locals_addr: usize }, } /// An evaluated place, together with its type. @@ -233,10 +235,10 @@ pub(super) fn place(&self) -> &Place { #[inline(always)] pub fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option)> { + ) -> Either, (mir::Local, Option, usize)> { match self.place { Place::Ptr(mplace) => Left(MPlaceTy { mplace, layout: self.layout }), - Place::Local { frame, local, offset } => Right((frame, local, offset)), + Place::Local { local, offset, locals_addr } => Right((local, offset, locals_addr)), } } @@ -279,7 +281,7 @@ fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( ) -> InterpResult<'tcx, Self> { Ok(match self.as_mplace_or_local() { Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(), - Right((frame, local, old_offset)) => { + Right((local, old_offset, locals_addr)) => { debug_assert!(layout.is_sized(), "unsized locals should live in memory"); assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway... // `Place::Local` are always in-bounds of their surrounding local, so we can just @@ -292,7 +294,10 @@ fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( .offset(old_offset.unwrap_or(Size::ZERO).bytes(), offset.bytes())?, ); - PlaceTy { place: Place::Local { frame, local, offset: Some(new_offset) }, layout } + PlaceTy { + place: Place::Local { local, offset: Some(new_offset), locals_addr }, + layout, + } } }) } @@ -331,7 +336,7 @@ pub fn assert_mem_place(&self) -> MPlaceTy<'tcx, Prov> { pub trait Writeable<'tcx, Prov: Provenance>: Projectable<'tcx, Prov> { fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)>; + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)>; fn force_mplace<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>( &self, @@ -343,9 +348,9 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for PlaceTy<'tcx, Prov> { #[inline(always)] fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)> { + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { self.as_mplace_or_local() - .map_right(|(frame, local, offset)| (frame, local, offset, self.layout)) + .map_right(|(local, offset, locals_addr)| (local, offset, locals_addr, self.layout)) } #[inline(always)] @@ -361,7 +366,7 @@ impl<'tcx, Prov: Provenance> Writeable<'tcx, Prov> for MPlaceTy<'tcx, Prov> { #[inline(always)] fn as_mplace_or_local( &self, - ) -> Either, (usize, mir::Local, Option, TyAndLayout<'tcx>)> { + ) -> Either, (mir::Local, Option, usize, TyAndLayout<'tcx>)> { Left(self.clone()) } @@ -501,21 +506,21 @@ pub fn mplace_to_simd( Ok((mplace, len)) } + /// Turn a local in the current frame into a place. pub fn local_to_place( &self, - frame: usize, local: mir::Local, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { // Other parts of the system rely on `Place::Local` never being unsized. // So we eagerly check here if this local has an MPlace, and if yes we use it. - let frame_ref = &self.stack()[frame]; - let layout = self.layout_of_local(frame_ref, local, None)?; + let frame = self.frame(); + let layout = self.layout_of_local(frame, local, None)?; let place = if layout.is_sized() { // We can just always use the `Local` for sized values. - Place::Local { frame, local, offset: None } + Place::Local { local, offset: None, locals_addr: frame.locals_addr() } } else { // Unsized `Local` isn't okay (we cannot store the metadata). - match frame_ref.locals[local].access()? { + match frame.locals[local].access()? { Operand::Immediate(_) => bug!(), Operand::Indirect(mplace) => Place::Ptr(*mplace), } @@ -530,7 +535,7 @@ pub fn eval_place( &self, mir_place: mir::Place<'tcx>, ) -> InterpResult<'tcx, PlaceTy<'tcx, M::Provenance>> { - let mut place = self.local_to_place(self.frame_idx(), mir_place.local)?; + let mut place = self.local_to_place(mir_place.local)?; // Using `try_fold` turned out to be bad for performance, hence the loop. for elem in mir_place.projection.iter() { place = self.project(&place, elem)? @@ -611,15 +616,15 @@ fn write_immediate_no_validate( // See if we can avoid an allocation. This is the counterpart to `read_immediate_raw`, // but not factored as a separate function. let mplace = match dest.as_mplace_or_local() { - Right((frame, local, offset, layout)) => { + Right((local, offset, locals_addr, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to // just fall back to the indirect path. dest.force_mplace(self)? } else { - M::before_access_local_mut(self, frame, local)?; - match self.stack_mut()[frame].locals[local].access_mut()? { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local_val) => { // Local can be updated in-place. *local_val = src; @@ -627,7 +632,7 @@ fn write_immediate_no_validate( // (*After* doing the update for borrow checker reasons.) if cfg!(debug_assertions) { let local_layout = - self.layout_of_local(&self.stack()[frame], local, None)?; + self.layout_of_local(&self.frame(), local, None)?; match (src, local_layout.abi) { (Immediate::Scalar(scalar), Abi::Scalar(s)) => { assert_eq!(scalar.size(), s.size(self)) @@ -725,7 +730,7 @@ pub fn write_uninit( ) -> InterpResult<'tcx> { let mplace = match dest.as_mplace_or_local() { Left(mplace) => mplace, - Right((frame, local, offset, layout)) => { + Right((local, offset, locals_addr, layout)) => { if offset.is_some() { // This has been projected to a part of this local. We could have complicated // logic to still keep this local as an `Operand`... but it's much easier to @@ -733,8 +738,8 @@ pub fn write_uninit( // FIXME: share the logic with `write_immediate_no_validate`. dest.force_mplace(self)? } else { - M::before_access_local_mut(self, frame, local)?; - match self.stack_mut()[frame].locals[local].access_mut()? { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + match self.frame_mut().locals[local].access_mut()? { Operand::Immediate(local) => { *local = Immediate::Uninit; return Ok(()); @@ -912,17 +917,16 @@ pub fn force_allocation( place: &PlaceTy<'tcx, M::Provenance>, ) -> InterpResult<'tcx, MPlaceTy<'tcx, M::Provenance>> { let mplace = match place.place { - Place::Local { frame, local, offset } => { - M::before_access_local_mut(self, frame, local)?; - let whole_local = match self.stack_mut()[frame].locals[local].access_mut()? { + Place::Local { local, offset, locals_addr } => { + debug_assert_eq!(locals_addr, self.frame().locals_addr()); + let whole_local = match self.frame_mut().locals[local].access_mut()? { &mut Operand::Immediate(local_val) => { // We need to make an allocation. // We need the layout of the local. We can NOT use the layout we got, // that might e.g., be an inner field of a struct with `Scalar` layout, // that has different alignment than the outer field. - let local_layout = - self.layout_of_local(&self.stack()[frame], local, None)?; + let local_layout = self.layout_of_local(&self.frame(), local, None)?; assert!(local_layout.is_sized(), "unsized locals cannot be immediate"); let mplace = self.allocate(local_layout, MemoryKind::Stack)?; // Preserve old value. (As an optimization, we can skip this if it was uninit.) @@ -936,11 +940,11 @@ pub fn force_allocation( mplace.mplace, )?; } - M::after_local_allocated(self, frame, local, &mplace)?; + M::after_local_allocated(self, local, &mplace)?; // Now we can call `access_mut` again, asserting it goes well, and actually // overwrite things. This points to the entire allocation, not just the part // the place refers to, i.e. we do this before we apply `offset`. - *self.stack_mut()[frame].locals[local].access_mut().unwrap() = + *self.frame_mut().locals[local].access_mut().unwrap() = Operand::Indirect(mplace.mplace); mplace.mplace } diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs index 7b68a37fdf3e..5ff78f7b8c90 100644 --- a/compiler/rustc_const_eval/src/interpret/projection.rs +++ b/compiler/rustc_const_eval/src/interpret/projection.rs @@ -357,7 +357,7 @@ pub fn project

(&self, base: &P, proj_elem: mir::PlaceElem<'tcx>) -> InterpRes Deref => self.deref_pointer(&base.to_op(self)?)?.into(), Index(local) => { let layout = self.layout_of(self.tcx.types.usize)?; - let n = self.local_to_op(self.frame(), local, Some(layout))?; + let n = self.local_to_op(local, Some(layout))?; let n = self.read_target_usize(&n)?; self.project_index(base, n)? } diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index bafb8cb0018c..82fb7ff1840f 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -631,7 +631,7 @@ pub(crate) fn eval_fn_call( body.args_iter() .map(|local| ( local, - self.layout_of_local(self.frame(), local, None).unwrap().ty + self.layout_of_local(self.frame(), local, None).unwrap().ty, )) .collect::>() ); diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index 086475f72c5d..c83ef14c03fe 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -1,14 +1,15 @@ -use crate::const_eval::CompileTimeEvalContext; +use crate::const_eval::{CompileTimeEvalContext, CompileTimeInterpreter, InterpretationResult}; use crate::interpret::{MemPlaceMeta, MemoryKind}; use rustc_hir::def_id::LocalDefId; -use rustc_middle::mir::interpret::{AllocId, Allocation, InterpResult, Pointer}; +use rustc_middle::mir; +use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, }; use std::ops::ControlFlow; -use super::MPlaceTy; +use super::{InterpCx, MPlaceTy}; /// Checks whether a type contains generic parameters which must be instantiated. /// @@ -80,11 +81,15 @@ fn visit_const(&mut self, c: ty::Const<'tcx>) -> Self::Result { } } -pub(crate) fn take_static_root_alloc<'mir, 'tcx: 'mir>( - ecx: &mut CompileTimeEvalContext<'mir, 'tcx>, - alloc_id: AllocId, -) -> Allocation { - ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1 +impl<'tcx> InterpretationResult<'tcx> for mir::interpret::ConstAllocation<'tcx> { + fn make_result<'mir>( + mplace: MPlaceTy<'tcx>, + ecx: &mut InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>, + ) -> Self { + let alloc_id = mplace.ptr().provenance.unwrap().alloc_id(); + let alloc = ecx.memory.alloc_map.swap_remove(&alloc_id).unwrap().1; + ecx.tcx.mk_const_alloc(alloc) + } } pub(crate) fn create_static_alloc<'mir, 'tcx: 'mir>( diff --git a/compiler/rustc_const_eval/src/lib.rs b/compiler/rustc_const_eval/src/lib.rs index 51836063945e..1e7ee208af1a 100644 --- a/compiler/rustc_const_eval/src/lib.rs +++ b/compiler/rustc_const_eval/src/lib.rs @@ -14,6 +14,7 @@ #![feature(generic_nonzero)] #![feature(let_chains)] #![feature(slice_ptr_get)] +#![feature(strict_provenance)] #![feature(never_type)] #![feature(trait_alias)] #![feature(try_blocks)] diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index c44883541357..938f9f0beaac 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -28,7 +28,7 @@ markdown, ColorConfig, DiagCtxt, ErrCode, ErrorGuaranteed, FatalError, PResult, }; use rustc_feature::find_gated_cfg; -use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; +use rustc_interface::util::{self, get_codegen_backend}; use rustc_interface::{interface, Queries}; use rustc_lint::unerased_lint_store; use rustc_metadata::creader::MetadataLoader; @@ -37,6 +37,7 @@ use rustc_session::config::{ErrorOutputType, Input, OutFileName, OutputType}; use rustc_session::getopts::{self, Matches}; use rustc_session::lint::{Lint, LintId}; +use rustc_session::output::collect_crate_types; use rustc_session::{config, filesearch, EarlyDiagCtxt, Session}; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::FileLoader; diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 0c3e7fb75b05..5d345e788e94 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -189,7 +189,8 @@ fn add_to_diag_with>( ); } -pub trait SubdiagMessageOp = Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; +pub trait SubdiagMessageOp = + Fn(&mut Diag<'_, G>, SubdiagMessage) -> SubdiagMessage; /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 57b8df52f4b7..4f033e3fefa0 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2086,7 +2086,7 @@ fn emit_messages_default( } if !self.short_message { for child in children { - assert!(child.level.can_be_top_or_sub().1); + assert!(child.level.can_be_subdiag()); let span = &child.span; if let Err(err) = self.emit_messages_default_inner( span, diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 286d4621850e..723f13dbe8d3 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -526,12 +526,15 @@ pub enum StashKey { UndeterminedMacroResolution, } -fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner)) { +fn default_track_diagnostic(diag: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { (*f)(diag) } -pub static TRACK_DIAGNOSTIC: AtomicRef = - AtomicRef::new(&(default_track_diagnostic as _)); +/// Diagnostics emitted by `DiagCtxtInner::emit_diagnostic` are passed through this function. Used +/// for tracking by incremental, to replay diagnostics as necessary. +pub static TRACK_DIAGNOSTIC: AtomicRef< + fn(DiagInner, &mut dyn FnMut(DiagInner) -> Option) -> Option, +> = AtomicRef::new(&(default_track_diagnostic as _)); #[derive(Copy, Clone, Default)] pub struct DiagCtxtFlags { @@ -1422,74 +1425,103 @@ fn emit_stashed_diagnostics(&mut self) -> Option { // Return value is only `Some` if the level is `Error` or `DelayedBug`. fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option { - assert!(diagnostic.level.can_be_top_or_sub().0); - - if let Some(expectation_id) = diagnostic.level.get_expectation_id() { - // The `LintExpectationId` can be stable or unstable depending on when it was created. - // Diagnostics created before the definition of `HirId`s are unstable and can not yet - // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by - // a stable one by the `LintLevelsBuilder`. - if let LintExpectationId::Unstable { .. } = expectation_id { - self.unstable_expect_diagnostics.push(diagnostic); - return None; - } - self.suppressed_expected_diag = true; - self.fulfilled_expectations.insert(expectation_id.normalize()); - } - if diagnostic.has_future_breakage() { // Future breakages aren't emitted if they're `Level::Allow`, // but they still need to be constructed and stashed below, // so they'll trigger the must_produce_diag check. - self.suppressed_expected_diag = true; + assert!(matches!(diagnostic.level, Error | Warning | Allow)); self.future_breakage_diagnostics.push(diagnostic.clone()); } - // Note that because this comes before the `match` below, - // `-Zeagerly-emit-delayed-bugs` continues to work even after we've - // issued an error and stopped recording new delayed bugs. - if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs { - diagnostic.level = Error; - } - + // We call TRACK_DIAGNOSTIC with an empty closure for the cases that + // return early *and* have some kind of side-effect, except where + // noted. match diagnostic.level { - // This must come after the possible promotion of `DelayedBug` to - // `Error` above. - Fatal | Error if self.treat_next_err_as_bug() => { - diagnostic.level = Bug; + Bug => {} + Fatal | Error => { + if self.treat_next_err_as_bug() { + // `Fatal` and `Error` can be promoted to `Bug`. + diagnostic.level = Bug; + } } DelayedBug => { - // If we have already emitted at least one error, we don't need - // to record the delayed bug, because it'll never be used. - return if let Some(guar) = self.has_errors() { - Some(guar) + // Note that because we check these conditions first, + // `-Zeagerly-emit-delayed-bugs` and `-Ztreat-err-as-bug` + // continue to work even after we've issued an error and + // stopped recording new delayed bugs. + if self.flags.eagerly_emit_delayed_bugs { + // `DelayedBug` can be promoted to `Error` or `Bug`. + if self.treat_next_err_as_bug() { + diagnostic.level = Bug; + } else { + diagnostic.level = Error; + } } else { - let backtrace = std::backtrace::Backtrace::capture(); - // This `unchecked_error_guaranteed` is valid. It is where the - // `ErrorGuaranteed` for delayed bugs originates. See - // `DiagCtxtInner::drop`. - #[allow(deprecated)] - let guar = ErrorGuaranteed::unchecked_error_guaranteed(); - self.delayed_bugs - .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); - Some(guar) - }; + // If we have already emitted at least one error, we don't need + // to record the delayed bug, because it'll never be used. + return if let Some(guar) = self.has_errors() { + Some(guar) + } else { + // No `TRACK_DIAGNOSTIC` call is needed, because the + // incremental session is deleted if there is a delayed + // bug. This also saves us from cloning the diagnostic. + let backtrace = std::backtrace::Backtrace::capture(); + // This `unchecked_error_guaranteed` is valid. It is where the + // `ErrorGuaranteed` for delayed bugs originates. See + // `DiagCtxtInner::drop`. + #[allow(deprecated)] + let guar = ErrorGuaranteed::unchecked_error_guaranteed(); + self.delayed_bugs + .push((DelayedDiagInner::with_backtrace(diagnostic, backtrace), guar)); + Some(guar) + }; + } } - Warning if !self.flags.can_emit_warnings => { + ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect` + Warning => { + if !self.flags.can_emit_warnings { + // We are not emitting warnings. + if diagnostic.has_future_breakage() { + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + } + return None; + } + } + Note | Help | FailureNote => {} + OnceNote | OnceHelp => panic!("bad level: {:?}", diagnostic.level), + Allow => { + // Nothing emitted for allowed lints. if diagnostic.has_future_breakage() { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); + // The side-effect is at the top of this method. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; } return None; } - Allow | Expect(_) => { - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {}); - return None; + Expect(expect_id) | ForceWarning(Some(expect_id)) => { + // Diagnostics created before the definition of `HirId`s are + // unstable and can not yet be stored. Instead, they are + // buffered until the `LintExpectationId` is replaced by a + // stable one by the `LintLevelsBuilder`. + if let LintExpectationId::Unstable { .. } = expect_id { + // We don't call TRACK_DIAGNOSTIC because we wait for the + // unstable ID to be updated, whereupon the diagnostic will + // be passed into this method again. + self.unstable_expect_diagnostics.push(diagnostic); + return None; + } + self.fulfilled_expectations.insert(expect_id.normalize()); + if let Expect(_) = diagnostic.level { + // Nothing emitted here for expected lints. + TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); + self.suppressed_expected_diag = true; + return None; + } } - _ => {} } - let mut guaranteed = None; - (*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| { + TRACK_DIAGNOSTIC(diagnostic, &mut |mut diagnostic| { if let Some(code) = diagnostic.code { self.emitted_diagnostic_codes.insert(code); } @@ -1552,17 +1584,17 @@ fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option bool { @@ -1863,23 +1895,13 @@ pub fn is_failure_note(&self) -> bool { matches!(*self, FailureNote) } - pub fn get_expectation_id(&self) -> Option { - match self { - Expect(id) | ForceWarning(Some(id)) => Some(*id), - _ => None, - } - } - - // Can this level be used in a top-level diagnostic message and/or a - // subdiagnostic message? - fn can_be_top_or_sub(&self) -> (bool, bool) { + // Can this level be used in a subdiagnostic message? + fn can_be_subdiag(&self) -> bool { match self { Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow - | Expect(_) => (true, false), + | Expect(_) => false, - Warning | Note | Help => (true, true), - - OnceNote | OnceHelp => (false, true), + Warning | Note | Help | OnceNote | OnceHelp => true, } } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 38848b22cb29..1f77484d65ce 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -894,56 +894,93 @@ pub struct BuiltinAttribute { ), rustc_attr!(TEST, rustc_insignificant_dtor, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_strict_coherence, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_variance_of_opaques, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), WarnFollowing), + rustc_attr!(TEST, rustc_variance, Normal, template!(Word), WarnFollowing, @only_local: true), + rustc_attr!( + TEST, rustc_variance_of_opaques, Normal, template!(Word), + WarnFollowing, @only_local: true + ), + rustc_attr!( + TEST, rustc_hidden_type_of_opaques, Normal, template!(Word), + WarnFollowing, @only_local: true), rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ..."), WarnFollowing), - rustc_attr!(TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), WarnFollowing), - rustc_attr!(TEST, rustc_regions, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_abi, Normal, template!(List: "field1, field2, ..."), + WarnFollowing, @only_local: true + ), + rustc_attr!( + TEST, rustc_regions, Normal, template!(Word), + WarnFollowing, @only_local: true + ), rustc_attr!( TEST, rustc_error, Normal, template!(Word, List: "delayed_bug_from_inside_query"), WarnFollowingWordOnly ), - rustc_attr!(TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_dump_user_args, Normal, template!(Word), WarnFollowing, + @only_local: true + ), rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word), WarnFollowing), rustc_attr!( - TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), DuplicatesOk + TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode"), + DuplicatesOk, @only_local: true ), rustc_attr!( - TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), DuplicatesOk + TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode"), + DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_clean, Normal, template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#), - DuplicatesOk, + DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_partition_reused, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_partition_codegened, Normal, - template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, + template!(List: r#"cfg = "...", module = "...""#), DuplicatesOk, @only_local: true ), rustc_attr!( TEST, rustc_expected_cgu_reuse, Normal, template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk, + @only_local: true + ), + rustc_attr!( + TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing, + @only_local: true ), - rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing), + rustc_attr!( + TEST, rustc_def_path, Normal, template!(Word), WarnFollowing, + @only_local: true + ), rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk), gated!( custom_mir, Normal, template!(List: r#"dialect = "...", phase = "...""#), - ErrorFollowing, "the `#[custom_mir]` attribute is just used for the Rust test suite", + ErrorFollowing, @only_local: true, + "the `#[custom_mir]` attribute is just used for the Rust test suite", + ), + rustc_attr!( + TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing, + @only_local: true + ), + rustc_attr!( + TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing, + @only_local: true + ), + rustc_attr!( + TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing, + @only_local: true ), - rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word), WarnFollowing), rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word), WarnFollowing), - rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk), + rustc_attr!( + TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/), DuplicatesOk, + @only_local: true + ), gated!( omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing, + @only_local: true, "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite", ), rustc_attr!( diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 7006c90e17b7..bb468b2e0567 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2553,6 +2553,11 @@ pub struct OpaqueTy<'hir> { pub in_trait: bool, } +#[derive(Copy, Clone, Debug, HashStable_Generic)] +pub struct AssocOpaqueTy { + // Add some data if necessary +} + /// From whence the opaque type came. #[derive(Copy, Clone, PartialEq, Eq, Debug, HashStable_Generic)] pub enum OpaqueTyOrigin { @@ -3363,6 +3368,7 @@ pub enum OwnerNode<'hir> { TraitItem(&'hir TraitItem<'hir>), ImplItem(&'hir ImplItem<'hir>), Crate(&'hir Mod<'hir>), + AssocOpaqueTy(&'hir AssocOpaqueTy), } impl<'hir> OwnerNode<'hir> { @@ -3372,7 +3378,7 @@ pub fn ident(&self) -> Option { | OwnerNode::ForeignItem(ForeignItem { ident, .. }) | OwnerNode::ImplItem(ImplItem { ident, .. }) | OwnerNode::TraitItem(TraitItem { ident, .. }) => Some(*ident), - OwnerNode::Crate(..) => None, + OwnerNode::Crate(..) | OwnerNode::AssocOpaqueTy(..) => None, } } @@ -3385,6 +3391,7 @@ pub fn span(&self) -> &'hir Span { | OwnerNode::ImplItem(ImplItem { span, .. }) | OwnerNode::TraitItem(TraitItem { span, .. }) => span, OwnerNode::Crate(Mod { spans: ModSpans { inner_span, .. }, .. }) => inner_span, + OwnerNode::AssocOpaqueTy(..) => unreachable!(), } } @@ -3443,6 +3450,7 @@ pub fn def_id(self) -> OwnerId { | OwnerNode::ImplItem(ImplItem { owner_id, .. }) | OwnerNode::ForeignItem(ForeignItem { owner_id, .. }) => *owner_id, OwnerNode::Crate(..) => crate::CRATE_HIR_ID.owner, + OwnerNode::AssocOpaqueTy(..) => unreachable!(), } } @@ -3486,6 +3494,7 @@ fn into(self) -> Node<'hir> { OwnerNode::ImplItem(n) => Node::ImplItem(n), OwnerNode::TraitItem(n) => Node::TraitItem(n), OwnerNode::Crate(n) => Node::Crate(n), + OwnerNode::AssocOpaqueTy(n) => Node::AssocOpaqueTy(n), } } } @@ -3523,6 +3532,7 @@ pub enum Node<'hir> { WhereBoundPredicate(&'hir WhereBoundPredicate<'hir>), // FIXME: Merge into `Node::Infer`. ArrayLenInfer(&'hir InferArg), + AssocOpaqueTy(&'hir AssocOpaqueTy), // Span by reference to minimize `Node`'s size #[allow(rustc::pass_by_value)] Err(&'hir Span), @@ -3573,6 +3583,7 @@ pub fn ident(&self) -> Option { | Node::Infer(..) | Node::WhereBoundPredicate(..) | Node::ArrayLenInfer(..) + | Node::AssocOpaqueTy(..) | Node::Err(..) => None, } } @@ -3678,6 +3689,7 @@ pub fn as_owner(self) -> Option> { Node::TraitItem(i) => Some(OwnerNode::TraitItem(i)), Node::ImplItem(i) => Some(OwnerNode::ImplItem(i)), Node::Crate(i) => Some(OwnerNode::Crate(i)), + Node::AssocOpaqueTy(i) => Some(OwnerNode::AssocOpaqueTy(i)), _ => None, } } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 09689b22e905..ae15efc0764b 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -196,6 +196,7 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) -> Result<(), ErrorG hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), }; if let Some(generics) = node.generics() { diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs index 7936621c8682..d1da2fa0fdc2 100644 --- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs +++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs @@ -262,6 +262,7 @@ fn resolve_bound_vars(tcx: TyCtxt<'_>, local_def_id: hir::OwnerId) -> ResolveBou visitor.visit_impl_item(item) } hir::OwnerNode::Crate(_) => {} + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), } let mut rl = ResolveBoundVars::default(); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 7d303d989c79..e070db9423df 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -121,6 +121,7 @@ fn print_node(&mut self, node: Node<'_>) { self.print_bounds(":", pred.bounds); } Node::ArrayLenInfer(_) => self.word("_"), + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(_) => self.word("/*ERROR*/"), } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 0bf81f8bae27..1fa1da93018a 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2174,7 +2174,9 @@ fn label_fn_like( let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] }; let node = self .tcx - .opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id)) + .opt_local_def_id_to_hir_id( + self.tcx.hir().get_parent_item(call_expr.hir_id).def_id, + ) .map(|hir_id| self.tcx.hir_node(hir_id)); match node { Some(hir::Node::Item(item)) => call_finder.visit_item(item), diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index dd9c16d006a9..1462037c8c8e 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -110,8 +110,9 @@ use rustc_data_structures::{base_n, flock}; use rustc_errors::ErrorGuaranteed; use rustc_fs_util::{link_or_copy, try_canonicalize, LinkOrCopy}; +use rustc_session::config::CrateType; +use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::{Session, StableCrateId}; -use rustc_span::Symbol; use std::fs as std_fs; use std::io::{self, ErrorKind}; @@ -205,11 +206,7 @@ pub fn in_incr_comp_dir(incr_comp_session_dir: &Path, file_name: &str) -> PathBu /// The garbage collection will take care of it. /// /// [`rustc_interface::queries::dep_graph`]: ../../rustc_interface/struct.Queries.html#structfield.dep_graph -pub(crate) fn prepare_session_directory( - sess: &Session, - crate_name: Symbol, - stable_crate_id: StableCrateId, -) -> Result<(), ErrorGuaranteed> { +pub(crate) fn prepare_session_directory(sess: &Session) -> Result<(), ErrorGuaranteed> { if sess.opts.incremental.is_none() { return Ok(()); } @@ -219,7 +216,7 @@ pub(crate) fn prepare_session_directory( debug!("prepare_session_directory"); // {incr-comp-dir}/{crate-name-and-disambiguator} - let crate_dir = crate_path(sess, crate_name, stable_crate_id); + let crate_dir = crate_path(sess); debug!("crate-dir: {}", crate_dir.display()); create_dir(sess, &crate_dir, "crate")?; @@ -604,9 +601,18 @@ fn string_to_timestamp(s: &str) -> Result { Ok(UNIX_EPOCH + duration) } -fn crate_path(sess: &Session, crate_name: Symbol, stable_crate_id: StableCrateId) -> PathBuf { +fn crate_path(sess: &Session) -> PathBuf { let incr_dir = sess.opts.incremental.as_ref().unwrap().clone(); + let crate_name = find_crate_name(sess, &[]); + let crate_types = collect_crate_types(sess, &[]); + let stable_crate_id = StableCrateId::new( + crate_name, + crate_types.contains(&CrateType::Executable), + sess.opts.cg.metadata.clone(), + sess.cfg_version, + ); + let stable_crate_id = base_n::encode(stable_crate_id.as_u64() as u128, INT_ENCODE_BASE); let crate_name = format!("{crate_name}-{stable_crate_id}"); diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs index 96bfe766c20f..357f2ae92d4c 100644 --- a/compiler/rustc_incremental/src/persist/load.rs +++ b/compiler/rustc_incremental/src/persist/load.rs @@ -8,8 +8,8 @@ use rustc_serialize::opaque::MemDecoder; use rustc_serialize::Decodable; use rustc_session::config::IncrementalStateAssertion; -use rustc_session::{Session, StableCrateId}; -use rustc_span::{ErrorGuaranteed, Symbol}; +use rustc_session::Session; +use rustc_span::ErrorGuaranteed; use std::path::{Path, PathBuf}; use super::data::*; @@ -190,13 +190,9 @@ pub fn load_query_result_cache(sess: &Session) -> Option> { /// Setups the dependency graph by loading an existing graph from disk and set up streaming of a /// new graph to an incremental session directory. -pub fn setup_dep_graph( - sess: &Session, - crate_name: Symbol, - stable_crate_id: StableCrateId, -) -> Result { +pub fn setup_dep_graph(sess: &Session) -> Result { // `load_dep_graph` can only be called after `prepare_session_directory`. - prepare_session_directory(sess, crate_name, stable_crate_id)?; + prepare_session_directory(sess)?; let res = sess.opts.build_dep_graph().then(|| load_dep_graph(sess)); diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 6fb2aa8b1b91..ef4b88575fce 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2554,6 +2554,7 @@ fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) { hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i), hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i), hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"), + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), } let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap(); diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index bd9fad8b0429..47dfbc1d7fbf 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -48,6 +48,3 @@ interface_rustc_error_unexpected_annotation = interface_temps_dir_error = failed to find or create the directory specified by `--temps-dir` - -interface_unsupported_crate_type_for_target = - dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs index f44ae705a3c2..a27f73789cde 100644 --- a/compiler/rustc_interface/src/callbacks.rs +++ b/compiler/rustc_interface/src/callbacks.rs @@ -29,7 +29,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) { /// This is a callback from `rustc_errors` as it cannot access the implicit state /// in `rustc_middle` otherwise. It is used when diagnostic messages are /// emitted and stores them in the current query, if there is one. -fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) { +fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner) -> R) -> R { tls::with_context_opt(|icx| { if let Some(icx) = icx { if let Some(diagnostics) = icx.diagnostics { @@ -38,11 +38,11 @@ fn track_diagnostic(diagnostic: DiagInner, f: &mut dyn FnMut(DiagInner)) { // Diagnostics are tracked, we can ignore the dependency. let icx = tls::ImplicitCtxt { task_deps: TaskDepsRef::Ignore, ..icx.clone() }; - return tls::enter_context(&icx, move || (*f)(diagnostic)); + tls::enter_context(&icx, move || (*f)(diagnostic)) + } else { + // In any other case, invoke diagnostics anyway. + (*f)(diagnostic) } - - // In any other case, invoke diagnostics anyway. - (*f)(diagnostic); }) } diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index a9ab2720d89a..29294003b8f2 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -1,7 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_session::config::CrateType; use rustc_span::{Span, Symbol}; -use rustc_target::spec::TargetTriple; use std::io; use std::path::Path; @@ -90,13 +88,6 @@ pub struct FailedWritingFile<'a> { #[diag(interface_proc_macro_crate_panic_abort)] pub struct ProcMacroCratePanicAbort; -#[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_target)] -pub struct UnsupportedCrateTypeForTarget<'a> { - pub crate_type: CrateType, - pub target_triple: &'a TargetTriple, -} - #[derive(Diagnostic)] #[diag(interface_multiple_output_types_adaption)] pub struct MultipleOutputTypesAdaption; diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index c22188226961..ee677a092e2f 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -18,7 +18,7 @@ use rustc_serialize::opaque::FileEncodeResult; use rustc_session::config::{self, CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::Untracked; -use rustc_session::output::find_crate_name; +use rustc_session::output::{collect_crate_types, find_crate_name}; use rustc_session::Session; use rustc_span::symbol::sym; use std::any::Any; @@ -128,7 +128,7 @@ pub fn global_ctxt(&'tcx self) -> Result> // parse `#[crate_name]` even if `--crate-name` was passed, to make sure it matches. let crate_name = find_crate_name(sess, &pre_configured_attrs); - let crate_types = util::collect_crate_types(sess, &pre_configured_attrs); + let crate_types = collect_crate_types(sess, &pre_configured_attrs); let stable_crate_id = StableCrateId::new( crate_name, crate_types.contains(&CrateType::Executable), @@ -136,7 +136,7 @@ pub fn global_ctxt(&'tcx self) -> Result> sess.cfg_version, ); let outputs = util::build_output_filenames(&pre_configured_attrs, sess); - let dep_graph = setup_dep_graph(sess, crate_name, stable_crate_id)?; + let dep_graph = setup_dep_graph(sess)?; let cstore = FreezeLock::new(Box::new(CStore::new( self.compiler.codegen_backend.metadata_loader(), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 829b00aabc12..7d48f90db362 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -7,14 +7,15 @@ use rustc_metadata::{load_symbol_from_dylib, DylibError}; use rustc_parse::validate_attr; use rustc_session as session; -use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes}; +use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes}; use rustc_session::filesearch::sysroot_candidates; use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer}; -use rustc_session::{filesearch, output, Session}; +use rustc_session::{filesearch, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_target::spec::Target; +use session::output::{categorize_crate_type, CRATE_TYPES}; use session::EarlyDiagCtxt; use std::env::consts::{DLL_PREFIX, DLL_SUFFIX}; use std::path::{Path, PathBuf}; @@ -399,67 +400,6 @@ pub(crate) fn check_attr_crate_type( } } -const CRATE_TYPES: &[(Symbol, CrateType)] = &[ - (sym::rlib, CrateType::Rlib), - (sym::dylib, CrateType::Dylib), - (sym::cdylib, CrateType::Cdylib), - (sym::lib, config::default_lib_output()), - (sym::staticlib, CrateType::Staticlib), - (sym::proc_dash_macro, CrateType::ProcMacro), - (sym::bin, CrateType::Executable), -]; - -fn categorize_crate_type(s: Symbol) -> Option { - Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) -} - -pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { - // If we're generating a test executable, then ignore all other output - // styles at all other locations - if session.opts.test { - return vec![CrateType::Executable]; - } - - // Only check command line flags if present. If no types are specified by - // command line, then reuse the empty `base` Vec to hold the types that - // will be found in crate attributes. - // JUSTIFICATION: before wrapper fn is available - #[allow(rustc::bad_opt_access)] - let mut base = session.opts.crate_types.clone(); - if base.is_empty() { - let attr_types = attrs.iter().filter_map(|a| { - if a.has_name(sym::crate_type) - && let Some(s) = a.value_str() - { - categorize_crate_type(s) - } else { - None - } - }); - base.extend(attr_types); - if base.is_empty() { - base.push(output::default_output_for_target(session)); - } else { - base.sort(); - base.dedup(); - } - } - - base.retain(|crate_type| { - if output::invalid_output_for_target(session, *crate_type) { - session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget { - crate_type: *crate_type, - target_triple: &session.opts.target_triple, - }); - false - } else { - true - } - }); - - base -} - fn multiple_output_types_to_stdout( output_types: &OutputTypes, single_output_file_is_stdout: bool, diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index d3d7698e7f92..506716e39a1e 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -356,7 +356,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), effective_visibilities: tcx.effective_visibilities(()), - last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id.into()), + last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id), generics: None, only_module: true, }; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index e89df1c9840c..95f312a31b33 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -103,11 +103,12 @@ fn raw_lint_id_level( mut idx: LintStackIndex, aux: Option<&FxIndexMap>, ) -> (Option, LintLevelSource) { - if let Some(specs) = aux { - if let Some(&(level, src)) = specs.get(&id) { - return (Some(level), src); - } + if let Some(specs) = aux + && let Some(&(level, src)) = specs.get(&id) + { + return (Some(level), src); } + loop { let LintSet { ref specs, parent } = self.list[idx]; if let Some(&(level, src)) = specs.get(&id) { @@ -177,7 +178,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe // There is only something to do if there are attributes at all. [] => {} // Most of the time, there is only one attribute. Avoid fetching HIR in that case. - [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }), + &[(local_id, _)] => levels.add_id(HirId { owner, local_id }), // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do // a standard visit. // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's. @@ -190,6 +191,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe levels.add_id(hir::CRATE_HIR_ID); levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID) } + hir::OwnerNode::AssocOpaqueTy(..) => unreachable!(), }, } @@ -643,63 +645,61 @@ fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) { // // This means that this only errors if we're truly lowering the lint // level from forbid. - if self.lint_added_lints && level != Level::Forbid { - if let Level::Forbid = old_level { - // Backwards compatibility check: - // - // We used to not consider `forbid(lint_group)` - // as preventing `allow(lint)` for some lint `lint` in - // `lint_group`. For now, issue a future-compatibility - // warning for this case. - let id_name = id.lint.name_lower(); - let fcw_warning = match old_src { - LintLevelSource::Default => false, - LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), - LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), - }; - debug!( - "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", - fcw_warning, - self.current_specs(), - old_src, - id_name - ); - let sub = match old_src { - LintLevelSource::Default => { - OverruledAttributeSub::DefaultSource { id: id.to_string() } - } - LintLevelSource::Node { span, reason, .. } => { - OverruledAttributeSub::NodeSource { span, reason } - } - LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource, - }; - if !fcw_warning { - self.sess.dcx().emit_err(OverruledAttribute { - span: src.span(), + if self.lint_added_lints && level != Level::Forbid && old_level == Level::Forbid { + // Backwards compatibility check: + // + // We used to not consider `forbid(lint_group)` + // as preventing `allow(lint)` for some lint `lint` in + // `lint_group`. For now, issue a future-compatibility + // warning for this case. + let id_name = id.lint.name_lower(); + let fcw_warning = match old_src { + LintLevelSource::Default => false, + LintLevelSource::Node { name, .. } => self.store.is_lint_group(name), + LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), + }; + debug!( + "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", + fcw_warning, + self.current_specs(), + old_src, + id_name + ); + let sub = match old_src { + LintLevelSource::Default => { + OverruledAttributeSub::DefaultSource { id: id.to_string() } + } + LintLevelSource::Node { span, reason, .. } => { + OverruledAttributeSub::NodeSource { span, reason } + } + LintLevelSource::CommandLine(_, _) => OverruledAttributeSub::CommandLineSource, + }; + if !fcw_warning { + self.sess.dcx().emit_err(OverruledAttribute { + span: src.span(), + overruled: src.span(), + lint_level: level.as_str(), + lint_source: src.name(), + sub, + }); + } else { + self.emit_span_lint( + FORBIDDEN_LINT_GROUPS, + src.span().into(), + OverruledAttributeLint { overruled: src.span(), lint_level: level.as_str(), lint_source: src.name(), sub, - }); - } else { - self.emit_span_lint( - FORBIDDEN_LINT_GROUPS, - src.span().into(), - OverruledAttributeLint { - overruled: src.span(), - lint_level: level.as_str(), - lint_source: src.name(), - sub, - }, - ); - } + }, + ); + } - // Retain the forbid lint level, unless we are - // issuing a FCW. In the FCW case, we want to - // respect the new setting. - if !fcw_warning { - return; - } + // Retain the forbid lint level, unless we are + // issuing a FCW. In the FCW case, we want to + // respect the new setting. + if !fcw_warning { + return; } } @@ -770,15 +770,15 @@ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: let Some(mut metas) = attr.meta_item_list() else { continue }; - if metas.is_empty() { + // Check whether `metas` is empty, and get its last element. + let Some(tail_li) = metas.last() else { // This emits the unused_attributes lint for `#[level()]` continue; - } + }; // Before processing the lint names, look for a reason (RFC 2383) // at the end. let mut reason = None; - let tail_li = &metas[metas.len() - 1]; if let Some(item) = tail_li.meta_item() { match item.kind { ast::MetaItemKind::Word => {} // actual lint names handled later @@ -834,21 +834,16 @@ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: let meta_item = match li { ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, _ => { - if let Some(item) = li.meta_item() { - if let ast::MetaItemKind::NameValue(_) = item.kind { - if item.path == sym::reason { - sess.dcx().emit_err(MalformedAttribute { - span: sp, - sub: MalformedAttributeSub::ReasonMustComeLast(sp), - }); - continue; - } - } - } - sess.dcx().emit_err(MalformedAttribute { - span: sp, - sub: MalformedAttributeSub::BadAttributeArgument(sp), - }); + let sub = if let Some(item) = li.meta_item() + && let ast::MetaItemKind::NameValue(_) = item.kind + && item.path == sym::reason + { + MalformedAttributeSub::ReasonMustComeLast(sp) + } else { + MalformedAttributeSub::BadAttributeArgument(sp) + }; + + sess.dcx().emit_err(MalformedAttribute { span: sp, sub }); continue; } }; @@ -987,11 +982,7 @@ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: } CheckLintNameResult::NoLint(suggestion) => { - let name = if let Some(tool_ident) = tool_ident { - format!("{}::{}", tool_ident.name, name) - } else { - name.to_string() - }; + let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name); let suggestion = suggestion.map(|(replace, from_rustc)| { UnknownLintSuggestion::WithSpan { suggestion: sp, replace, from_rustc } }); @@ -1005,27 +996,24 @@ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: if let CheckLintNameResult::Renamed(new_name) = lint_result { // Ignore any errors or warnings that happen because the new name is inaccurate // NOTE: `new_name` already includes the tool name, so we don't have to add it again. - if let CheckLintNameResult::Ok(ids) = + let CheckLintNameResult::Ok(ids) = self.store.check_lint_name(&new_name, None, self.registered_tools) - { - let src = LintLevelSource::Node { - name: Symbol::intern(&new_name), - span: sp, - reason, - }; - for &id in ids { - if self.check_gated_lint(id, attr.span, false) { - self.insert_spec(id, (level, src)); - } - } - if let Level::Expect(expect_id) = level { - self.provider.push_expectation( - expect_id, - LintExpectation::new(reason, sp, false, tool_name), - ); - } - } else { + else { panic!("renamed lint does not exist: {new_name}"); + }; + + let src = + LintLevelSource::Node { name: Symbol::intern(&new_name), span: sp, reason }; + for &id in ids { + if self.check_gated_lint(id, attr.span, false) { + self.insert_spec(id, (level, src)); + } + } + if let Level::Expect(expect_id) = level { + self.provider.push_expectation( + expect_id, + LintExpectation::new(reason, sp, false, tool_name), + ); } } } @@ -1058,38 +1046,44 @@ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: /// Returns `true` if the lint's feature is enabled. #[track_caller] fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool { - if let Some(feature) = lint_id.lint.feature_gate { - if !self.features.active(feature) { - if self.lint_added_lints { - let lint = builtin::UNKNOWN_LINTS; - let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); - // FIXME: make this translatable - #[allow(rustc::diagnostic_outside_of_impl)] - #[allow(rustc::untranslatable_diagnostic)] - lint_level( - self.sess, + let feature = if let Some(feature) = lint_id.lint.feature_gate + && !self.features.active(feature) + { + // Lint is behind a feature that is not enabled; eventually return false. + feature + } else { + // Lint is ungated or its feature is enabled; exit early. + return true; + }; + + if self.lint_added_lints { + let lint = builtin::UNKNOWN_LINTS; + let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + lint_level( + self.sess, + lint, + level, + src, + Some(span.into()), + fluent::lint_unknown_gated_lint, + |lint| { + lint.arg("name", lint_id.lint.name_lower()); + lint.note(fluent::lint_note); + rustc_session::parse::add_feature_diagnostics_for_issue( lint, - level, - src, - Some(span.into()), - fluent::lint_unknown_gated_lint, - |lint| { - lint.arg("name", lint_id.lint.name_lower()); - lint.note(fluent::lint_note); - rustc_session::parse::add_feature_diagnostics_for_issue( - lint, - &self.sess, - feature, - GateIssue::Language, - lint_from_cli, - ); - }, + &self.sess, + feature, + GateIssue::Language, + lint_from_cli, ); - } - return false; - } + }, + ); } - true + + false } /// Find the lint level for a lint. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 20e492dbd8a7..6506aa334310 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -557,6 +557,7 @@ /// fn main() { /// use foo::bar; /// foo::bar(); + /// bar(); /// } /// ``` /// @@ -704,6 +705,20 @@ /// `PhantomData`. /// /// Otherwise consider removing the unused code. + /// + /// ### Limitations + /// + /// Removing fields that are only used for side-effects and never + /// read will result in behavioral changes. Examples of this + /// include: + /// + /// - If a field's value performs an action when it is dropped. + /// - If a field's type does not implement an auto trait + /// (e.g. `Send`, `Sync`, `Unpin`). + /// + /// For side-effects from dropping field values, this lint should + /// be allowed on those fields. For side-effects from containing + /// field types, `PhantomData` should be used. pub DEAD_CODE, Warn, "detect unused, unexported items" @@ -4341,7 +4356,6 @@ pub UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, Warn, "unrecognized or malformed diagnostic attribute", - @feature_gate = sym::diagnostic_namespace; } declare_lint! { diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index a532635669df..c90427256b85 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -115,6 +115,7 @@ macro_rules! arena_types { [] features: rustc_feature::Features, [decode] specialization_graph: rustc_middle::traits::specialization_graph::Graph, [] crate_inherent_impls: rustc_middle::ty::CrateInherentImpls, + [] hir_owner_nodes: rustc_hir::OwnerNodes<'tcx>, ]); ) } diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 142988dee747..cc940f2e74b8 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -161,7 +161,7 @@ pub fn hir_owner_node(self, owner_id: OwnerId) -> OwnerNode<'tcx> { /// Retrieves the `hir::Node` corresponding to `id`, returning `None` if cannot be found. #[inline] pub fn opt_hir_node_by_def_id(self, id: LocalDefId) -> Option> { - Some(self.hir_node(self.opt_local_def_id_to_hir_id(id)?)) + Some(self.hir_node_by_def_id(id)) } /// Retrieves the `hir::Node` corresponding to `id`. @@ -169,12 +169,10 @@ pub fn hir_node(self, id: HirId) -> Node<'tcx> { self.hir_owner_nodes(id.owner).nodes[id.local_id].node } - /// Retrieves the `hir::Node` corresponding to `id`, panicking if it cannot be found. + /// Retrieves the `hir::Node` corresponding to `id`. #[inline] - #[track_caller] pub fn hir_node_by_def_id(self, id: LocalDefId) -> Node<'tcx> { - self.opt_hir_node_by_def_id(id) - .unwrap_or_else(|| bug!("couldn't find HIR node for def id {id:?}")) + self.hir_node(self.local_def_id_to_hir_id(id)) } /// Returns `HirId` of the parent HIR node of node with this `hir_id`. @@ -963,6 +961,7 @@ pub fn span_with_body(self, hir_id: HirId) -> Span { Node::Crate(item) => item.spans.inner_span, Node::WhereBoundPredicate(pred) => pred.span, Node::ArrayLenInfer(inf) => inf.span, + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(span) => *span, } } @@ -1227,6 +1226,7 @@ fn hir_id_to_string(map: Map<'_>, id: HirId) -> String { Node::Crate(..) => String::from("(root_crate)"), Node::WhereBoundPredicate(_) => node_str("where bound predicate"), Node::ArrayLenInfer(_) => node_str("array len infer"), + Node::AssocOpaqueTy(..) => unreachable!(), Node::Err(_) => node_str("error"), } } diff --git a/compiler/rustc_middle/src/hir/mod.rs b/compiler/rustc_middle/src/hir/mod.rs index 4ef9bc16221f..f9fa8ac2f7aa 100644 --- a/compiler/rustc_middle/src/hir/mod.rs +++ b/compiler/rustc_middle/src/hir/mod.rs @@ -8,6 +8,9 @@ use crate::query::Providers; use crate::ty::{EarlyBinder, ImplSubject, TyCtxt}; +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::sorted_map::SortedMap; +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_data_structures::sync::{try_par_for_each_in, DynSend, DynSync}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; @@ -121,18 +124,40 @@ pub fn is_foreign_item(self, def_id: impl Into) -> bool { self.opt_parent(def_id.into()) .is_some_and(|parent| matches!(self.def_kind(parent), DefKind::ForeignMod)) } + + pub fn hash_owner_nodes( + self, + node: OwnerNode<'_>, + bodies: &SortedMap>, + attrs: &SortedMap, + ) -> (Option, Option) { + if self.needs_crate_hash() { + self.with_stable_hashing_context(|mut hcx| { + let mut stable_hasher = StableHasher::new(); + node.hash_stable(&mut hcx, &mut stable_hasher); + // Bodies are stored out of line, so we need to pull them explicitly in the hash. + bodies.hash_stable(&mut hcx, &mut stable_hasher); + let h1 = stable_hasher.finish(); + + let mut stable_hasher = StableHasher::new(); + attrs.hash_stable(&mut hcx, &mut stable_hasher); + let h2 = stable_hasher.finish(); + (Some(h1), Some(h2)) + }) + } else { + (None, None) + } + } } pub fn provide(providers: &mut Providers) { providers.hir_crate_items = map::hir_crate_items; providers.crate_hash = map::crate_hash; providers.hir_module_items = map::hir_module_items; - providers.opt_local_def_id_to_hir_id = |tcx, def_id| { - Some(match tcx.hir_crate(()).owners[def_id] { - MaybeOwner::Owner(_) => HirId::make_owner(def_id), - MaybeOwner::NonOwner(hir_id) => hir_id, - MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id), - }) + providers.local_def_id_to_hir_id = |tcx, def_id| match tcx.hir_crate(()).owners[def_id] { + MaybeOwner::Owner(_) => HirId::make_owner(def_id), + MaybeOwner::NonOwner(hir_id) => hir_id, + MaybeOwner::Phantom => bug!("No HirId for {:?}", def_id), }; providers.opt_hir_owner_nodes = |tcx, id| tcx.hir_crate(()).owners.get(id)?.as_owner().map(|i| &i.nodes); diff --git a/compiler/rustc_middle/src/hooks/mod.rs b/compiler/rustc_middle/src/hooks/mod.rs index 8588ae203361..b984df3646e6 100644 --- a/compiler/rustc_middle/src/hooks/mod.rs +++ b/compiler/rustc_middle/src/hooks/mod.rs @@ -6,6 +6,7 @@ use crate::mir; use crate::query::TyCtxtAt; use crate::ty::{Ty, TyCtxt}; +use rustc_span::def_id::LocalDefId; use rustc_span::DUMMY_SP; macro_rules! declare_hooks { @@ -70,4 +71,10 @@ fn clone(&self) -> Self { *self } /// Getting a &core::panic::Location referring to a span. hook const_caller_location(file: rustc_span::Symbol, line: u32, col: u32) -> mir::ConstValue<'tcx>; + + /// Returns `true` if this def is a function-like thing that is eligible for + /// coverage instrumentation under `-Cinstrument-coverage`. + /// + /// (Eligible functions might nevertheless be skipped for other reasons.) + hook is_eligible_for_coverage(key: LocalDefId) -> bool; } diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs index 18e198eb9fca..645a417c3225 100644 --- a/compiler/rustc_middle/src/mir/coverage.rs +++ b/compiler/rustc_middle/src/mir/coverage.rs @@ -2,10 +2,19 @@ use rustc_index::IndexVec; use rustc_macros::HashStable; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; use std::fmt::{self, Debug, Formatter}; +rustc_index::newtype_index! { + /// Used by [`CoverageKind::BlockMarker`] to mark blocks during THIR-to-MIR + /// lowering, so that those blocks can be identified later. + #[derive(HashStable)] + #[encodable] + #[debug_format = "BlockMarkerId({})"] + pub struct BlockMarkerId {} +} + rustc_index::newtype_index! { /// ID of a coverage counter. Values ascend from 0. /// @@ -83,6 +92,12 @@ pub enum CoverageKind { /// codegen. SpanMarker, + /// Marks its enclosing basic block with an ID that can be referred to by + /// side data in [`BranchInfo`]. + /// + /// Has no effect during codegen. + BlockMarker { id: BlockMarkerId }, + /// Marks the point in MIR control flow represented by a coverage counter. /// /// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR. @@ -107,6 +122,7 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { use CoverageKind::*; match self { SpanMarker => write!(fmt, "SpanMarker"), + BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()), CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()), ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()), } @@ -163,14 +179,18 @@ pub struct Expression { pub enum MappingKind { /// Associates a normal region of code with a counter/expression/zero. Code(CovTerm), + /// Associates a branch region with separate counters for true and false. + Branch { true_term: CovTerm, false_term: CovTerm }, } impl MappingKind { /// Iterator over all coverage terms in this mapping kind. pub fn terms(&self) -> impl Iterator { - let one = |a| std::iter::once(a); + let one = |a| std::iter::once(a).chain(None); + let two = |a, b| std::iter::once(a).chain(Some(b)); match *self { Self::Code(term) => one(term), + Self::Branch { true_term, false_term } => two(true_term, false_term), } } @@ -179,6 +199,9 @@ pub fn terms(&self) -> impl Iterator { pub fn map_terms(&self, map_fn: impl Fn(CovTerm) -> CovTerm) -> Self { match *self { Self::Code(term) => Self::Code(map_fn(term)), + Self::Branch { true_term, false_term } => { + Self::Branch { true_term: map_fn(true_term), false_term: map_fn(false_term) } + } } } } @@ -202,3 +225,22 @@ pub struct FunctionCoverageInfo { pub expressions: IndexVec, pub mappings: Vec, } + +/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR. +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct BranchInfo { + /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was + /// injected into the MIR body. This makes it possible to allocate per-ID + /// data structures without having to scan the entire body first. + pub num_block_markers: usize, + pub branch_spans: Vec, +} + +#[derive(Clone, Debug)] +#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)] +pub struct BranchSpan { + pub span: Span, + pub true_marker: BlockMarkerId, + pub false_marker: BlockMarkerId, +} diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index b71c614dc4fe..d57ffc0f8b5c 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -403,6 +403,12 @@ pub struct Body<'tcx> { pub tainted_by_errors: Option, + /// Branch coverage information collected during MIR building, to be used by + /// the `InstrumentCoverage` pass. + /// + /// Only present if branch coverage is enabled and this function is eligible. + pub coverage_branch_info: Option>, + /// Per-function coverage information added by the `InstrumentCoverage` /// pass, to be used in conjunction with the coverage statements injected /// into this body's blocks. @@ -450,6 +456,7 @@ pub fn new( is_polymorphic: false, injection_phase: None, tainted_by_errors, + coverage_branch_info: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); @@ -479,6 +486,7 @@ pub fn new_cfg_only(basic_blocks: IndexVec>) -> is_polymorphic: false, injection_phase: None, tainted_by_errors: None, + coverage_branch_info: None, function_coverage_info: None, }; body.is_polymorphic = body.has_non_region_param(); diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 8ae65f3832fd..94751c447615 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -461,6 +461,9 @@ pub fn write_mir_intro<'tcx>( // Add an empty line before the first block is printed. writeln!(w)?; + if let Some(branch_info) = &body.coverage_branch_info { + write_coverage_branch_info(branch_info, w)?; + } if let Some(function_coverage_info) = &body.function_coverage_info { write_function_coverage_info(function_coverage_info, w)?; } @@ -468,6 +471,25 @@ pub fn write_mir_intro<'tcx>( Ok(()) } +fn write_coverage_branch_info( + branch_info: &coverage::BranchInfo, + w: &mut dyn io::Write, +) -> io::Result<()> { + let coverage::BranchInfo { branch_spans, .. } = branch_info; + + for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans { + writeln!( + w, + "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}", + )?; + } + if !branch_spans.is_empty() { + writeln!(w)?; + } + + Ok(()) +} + fn write_function_coverage_info( function_coverage_info: &coverage::FunctionCoverageInfo, w: &mut dyn io::Write, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 83ded5859c67..865299e15c80 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -174,10 +174,8 @@ cache_on_disk_if { true } } - /// Gives access to the HIR ID for the given `LocalDefId` owner `key` if any. - /// - /// Definitions that were generated with no HIR, would be fed to return `None`. - query opt_local_def_id_to_hir_id(key: LocalDefId) -> Option{ + /// Returns HIR ID for the given `LocalDefId`. + query local_def_id_to_hir_id(key: LocalDefId) -> hir::HirId { desc { |tcx| "getting HIR ID of `{}`", tcx.def_path_str(key) } feedable } @@ -196,6 +194,7 @@ /// Avoid calling this query directly. query opt_hir_owner_nodes(key: LocalDefId) -> Option<&'tcx hir::OwnerNodes<'tcx>> { desc { |tcx| "getting HIR owner items in `{}`", tcx.def_path_str(key) } + feedable } /// Gives access to the HIR attributes inside the HIR owner `key`. @@ -204,6 +203,7 @@ /// Avoid calling this query directly. query hir_attrs(key: hir::OwnerId) -> &'tcx hir::AttributeMap<'tcx> { desc { |tcx| "getting HIR owner attributes in `{}`", tcx.def_path_str(key) } + feedable } /// Given the def_id of a const-generic parameter, computes the associated default const diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index aea586533512..a04bd636622e 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -618,6 +618,7 @@ pub enum SelectionError<'tcx> { OpaqueTypeAutoTraitLeakageUnknown(DefId), } +// FIXME(@lcnr): The `Binder` here should be unnecessary. Just use `TraitRef` instead. #[derive(Clone, Debug, TypeVisitable)] pub struct SignatureMismatchData<'tcx> { pub found_trait_ref: ty::PolyTraitRef<'tcx>, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index c415c06c21b9..5362b6d8b24c 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -589,6 +589,11 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> { pub fn def_id(&self) -> LocalDefId { self.key } + + // Caller must ensure that `self.key` ID is indeed an owner. + pub fn feed_owner_id(&self) -> TyCtxtFeed<'tcx, hir::OwnerId> { + TyCtxtFeed { tcx: self.tcx, key: hir::OwnerId { def_id: self.key } } + } } /// The central data structure of the compiler. It stores references @@ -2350,8 +2355,8 @@ pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option HirId { - self.opt_local_def_id_to_hir_id(local_def_id).unwrap() + pub fn opt_local_def_id_to_hir_id(self, local_def_id: LocalDefId) -> Option { + Some(self.local_def_id_to_hir_id(local_def_id)) } pub fn next_trait_solver_globally(self) -> bool { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index c8fb11673cf8..4b015640f916 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -405,6 +405,7 @@ fn fmt>>( ::rustc_hir::HirId, ::rustc_hir::MatchSource, ::rustc_target::asm::InlineAsmRegOrRegClass, + crate::mir::coverage::BlockMarkerId, crate::mir::coverage::CounterId, crate::mir::coverage::ExpressionId, crate::mir::Local, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index cac12e5ee0bb..11065b2a382b 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -2436,8 +2436,9 @@ pub fn to_opt_closure_kind(self) -> Option { }, // "Bound" types appear in canonical queries when the - // closure type is not yet known - Bound(..) | Param(_) | Infer(_) => None, + // closure type is not yet known, and `Placeholder` and `Param` + // may be encountered in generic `AsyncFnKindHelper` goals. + Bound(..) | Placeholder(_) | Param(_) | Infer(_) => None, Error(_) => Some(ty::ClosureKind::Fn), diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs new file mode 100644 index 000000000000..0b8ec234dda7 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs @@ -0,0 +1,148 @@ +use std::assert_matches::assert_matches; +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; +use rustc_middle::mir::{self, BasicBlock, UnOp}; +use rustc_middle::thir::{ExprId, ExprKind, Thir}; +use rustc_middle::ty::TyCtxt; +use rustc_span::def_id::LocalDefId; + +use crate::build::Builder; + +pub(crate) struct BranchInfoBuilder { + /// Maps condition expressions to their enclosing `!`, for better instrumentation. + nots: FxHashMap, + + num_block_markers: usize, + branch_spans: Vec, +} + +#[derive(Clone, Copy)] +struct NotInfo { + /// When visiting the associated expression as a branch condition, treat this + /// enclosing `!` as the branch condition instead. + enclosing_not: ExprId, + /// True if the associated expression is nested within an odd number of `!` + /// expressions relative to `enclosing_not` (inclusive of `enclosing_not`). + is_flipped: bool, +} + +impl BranchInfoBuilder { + /// Creates a new branch info builder, but only if branch coverage instrumentation + /// is enabled and `def_id` represents a function that is eligible for coverage. + pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option { + if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) { + Some(Self { nots: FxHashMap::default(), num_block_markers: 0, branch_spans: vec![] }) + } else { + None + } + } + + /// Unary `!` expressions inside an `if` condition are lowered by lowering + /// their argument instead, and then reversing the then/else arms of that `if`. + /// + /// That's awkward for branch coverage instrumentation, so to work around that + /// we pre-emptively visit any affected `!` expressions, and record extra + /// information that [`Builder::visit_coverage_branch_condition`] can use to + /// synthesize branch instrumentation for the enclosing `!`. + pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) { + assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. }); + + self.visit_with_not_info( + thir, + unary_not, + // Set `is_flipped: false` for the `!` itself, so that its enclosed + // expression will have `is_flipped: true`. + NotInfo { enclosing_not: unary_not, is_flipped: false }, + ); + } + + fn visit_with_not_info(&mut self, thir: &Thir<'_>, expr_id: ExprId, not_info: NotInfo) { + match self.nots.entry(expr_id) { + // This expression has already been marked by an enclosing `!`. + Entry::Occupied(_) => return, + Entry::Vacant(entry) => entry.insert(not_info), + }; + + match thir[expr_id].kind { + ExprKind::Unary { op: UnOp::Not, arg } => { + // Invert the `is_flipped` flag for the contents of this `!`. + let not_info = NotInfo { is_flipped: !not_info.is_flipped, ..not_info }; + self.visit_with_not_info(thir, arg, not_info); + } + ExprKind::Scope { value, .. } => self.visit_with_not_info(thir, value, not_info), + ExprKind::Use { source } => self.visit_with_not_info(thir, source, not_info), + // All other expressions (including `&&` and `||`) don't need any + // special handling of their contents, so stop visiting. + _ => {} + } + } + + fn next_block_marker_id(&mut self) -> BlockMarkerId { + let id = BlockMarkerId::from_usize(self.num_block_markers); + self.num_block_markers += 1; + id + } + + pub(crate) fn into_done(self) -> Option> { + let Self { nots: _, num_block_markers, branch_spans } = self; + + if num_block_markers == 0 { + assert!(branch_spans.is_empty()); + return None; + } + + Some(Box::new(mir::coverage::BranchInfo { num_block_markers, branch_spans })) + } +} + +impl Builder<'_, '_> { + /// If branch coverage is enabled, inject marker statements into `then_block` + /// and `else_block`, and record their IDs in the table of branch spans. + pub(crate) fn visit_coverage_branch_condition( + &mut self, + mut expr_id: ExprId, + mut then_block: BasicBlock, + mut else_block: BasicBlock, + ) { + // Bail out if branch coverage is not enabled for this function. + let Some(branch_info) = self.coverage_branch_info.as_ref() else { return }; + + // If this condition expression is nested within one or more `!` expressions, + // replace it with the enclosing `!` collected by `visit_unary_not`. + if let Some(&NotInfo { enclosing_not, is_flipped }) = branch_info.nots.get(&expr_id) { + expr_id = enclosing_not; + if is_flipped { + std::mem::swap(&mut then_block, &mut else_block); + } + } + let source_info = self.source_info(self.thir[expr_id].span); + + // Now that we have `source_info`, we can upgrade to a &mut reference. + let branch_info = self.coverage_branch_info.as_mut().expect("upgrading & to &mut"); + + let mut inject_branch_marker = |block: BasicBlock| { + let id = branch_info.next_block_marker_id(); + + let marker_statement = mir::Statement { + source_info, + kind: mir::StatementKind::Coverage(Box::new(mir::Coverage { + kind: CoverageKind::BlockMarker { id }, + })), + }; + self.cfg.push(block, marker_statement); + + id + }; + + let true_marker = inject_branch_marker(then_block); + let false_marker = inject_branch_marker(else_block); + + branch_info.branch_spans.push(BranchSpan { + span: source_info.span, + true_marker, + false_marker, + }); + } +} diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs index c2bff9084c67..288b787798b4 100644 --- a/compiler/rustc_mir_build/src/build/custom/mod.rs +++ b/compiler/rustc_mir_build/src/build/custom/mod.rs @@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>( tainted_by_errors: None, injection_phase: None, pass_count: 0, + coverage_branch_info: None, function_coverage_info: None, }; diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs index 4d5ed65c8416..e7808ff880b5 100644 --- a/compiler/rustc_mir_build/src/build/matches/mod.rs +++ b/compiler/rustc_mir_build/src/build/matches/mod.rs @@ -105,6 +105,13 @@ fn then_else_break_inner( success_block.unit() } ExprKind::Unary { op: UnOp::Not, arg } => { + // Improve branch coverage instrumentation by noting conditions + // nested within one or more `!` expressions. + // (Skipped if branch coverage is not enabled.) + if let Some(branch_info) = this.coverage_branch_info.as_mut() { + branch_info.visit_unary_not(this.thir, expr_id); + } + let local_scope = this.local_scope(); let (success_block, failure_block) = this.in_if_then_scope(local_scope, expr_span, |this| { @@ -149,6 +156,10 @@ fn then_else_break_inner( let else_block = this.cfg.start_new_block(); let term = TerminatorKind::if_(operand, then_block, else_block); + // Record branch coverage info for this condition. + // (Does nothing if branch coverage is not enabled.) + this.visit_coverage_branch_condition(expr_id, then_block, else_block); + let source_info = this.source_info(expr_span); this.cfg.terminate(block, source_info, term); this.break_for_else(else_block, source_info); diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs index 45954bdb114f..2fe5d3fb5e0d 100644 --- a/compiler/rustc_mir_build/src/build/mod.rs +++ b/compiler/rustc_mir_build/src/build/mod.rs @@ -234,6 +234,10 @@ struct Builder<'a, 'tcx> { // the root (most of them do) and saves us from retracing many sub-paths // many times, and rechecking many nodes. lint_level_roots_cache: GrowableBitSet, + + /// Collects additional coverage information during MIR building. + /// Only present if branch coverage is enabled and this function is eligible. + coverage_branch_info: Option, } type CaptureMap<'tcx> = SortedIndexMultiMap>; @@ -807,6 +811,7 @@ fn new( unit_temp: None, var_debug_info: vec![], lint_level_roots_cache: GrowableBitSet::new_empty(), + coverage_branch_info: coverageinfo::BranchInfoBuilder::new_if_enabled(tcx, def), }; assert_eq!(builder.cfg.start_new_block(), START_BLOCK); @@ -826,7 +831,7 @@ fn finish(self) -> Body<'tcx> { } } - Body::new( + let mut body = Body::new( MirSource::item(self.def_id.to_def_id()), self.cfg.basic_blocks, self.source_scopes, @@ -837,7 +842,9 @@ fn finish(self) -> Body<'tcx> { self.fn_span, self.coroutine, None, - ) + ); + body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done()); + body } fn insert_upvar_arg(&mut self) { @@ -1111,6 +1118,7 @@ pub(crate) fn parse_float_into_scalar( mod block; mod cfg; +mod coverageinfo; mod custom; mod expr; mod matches; diff --git a/compiler/rustc_mir_transform/src/add_retag.rs b/compiler/rustc_mir_transform/src/add_retag.rs index 430d9572e759..6f668aa4ce85 100644 --- a/compiler/rustc_mir_transform/src/add_retag.rs +++ b/compiler/rustc_mir_transform/src/add_retag.rs @@ -118,7 +118,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } // PART 3 - // Add retag after assignments where data "enters" this function: the RHS is behind a deref and the LHS is not. + // Add retag after assignments. for block_data in basic_blocks { // We want to insert statements as we iterate. To this end, we // iterate backwards using indices. diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index 4c5be0a3f4be..b2407c545071 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -14,7 +14,6 @@ use crate::MirPass; use rustc_middle::hir; -use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::*; use rustc_middle::mir::{ self, BasicBlock, BasicBlockData, Coverage, SourceInfo, Statement, StatementKind, Terminator, @@ -44,7 +43,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { let def_id = mir_source.def_id().expect_local(); - if !is_eligible_for_coverage(tcx, def_id) { + if !tcx.is_eligible_for_coverage(def_id) { trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)"); return; } @@ -140,6 +139,10 @@ fn create_mappings<'tcx>( .filter_map(|&BcbMapping { kind: bcb_mapping_kind, span }| { let kind = match bcb_mapping_kind { BcbMappingKind::Code(bcb) => MappingKind::Code(term_for_bcb(bcb)), + BcbMappingKind::Branch { true_bcb, false_bcb } => MappingKind::Branch { + true_term: term_for_bcb(true_bcb), + false_term: term_for_bcb(false_bcb), + }, }; let code_region = make_code_region(source_map, file_name, span, body_span)?; Some(Mapping { kind, code_region }) @@ -349,37 +352,6 @@ fn check_code_region(code_region: CodeRegion) -> Option { } } -fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { - // Only instrument functions, methods, and closures (not constants since they are evaluated - // at compile time by Miri). - // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const - // expressions get coverage spans, we will probably have to "carve out" space for const - // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might - // be tricky if const expressions have no corresponding statements in the enclosing MIR. - // Closures are carved out by their initial `Assign` statement.) - if !tcx.def_kind(def_id).is_fn_like() { - trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)"); - return false; - } - - // Don't instrument functions with `#[automatically_derived]` on their - // enclosing impl block, on the assumption that most users won't care about - // coverage for derived impls. - if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) - && tcx.is_automatically_derived(impl_of) - { - trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); - return false; - } - - if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { - trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)"); - return false; - } - - true -} - /// Function information extracted from HIR by the coverage instrumentor. #[derive(Debug)] struct ExtractedHirInfo { diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index dfc7c3a713bc..1de7b6f66a7b 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,14 +1,49 @@ -use super::*; - use rustc_data_structures::captures::Captures; -use rustc_middle::mir::coverage::*; -use rustc_middle::mir::{Body, CoverageIdsInfo}; -use rustc_middle::query::Providers; -use rustc_middle::ty::{self}; +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::query::TyCtxtAt; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::util::Providers; +use rustc_span::def_id::LocalDefId; -/// A `query` provider for retrieving coverage information injected into MIR. +/// Registers query/hook implementations related to coverage. pub(crate) fn provide(providers: &mut Providers) { - providers.coverage_ids_info = |tcx, def_id| coverage_ids_info(tcx, def_id); + providers.hooks.is_eligible_for_coverage = + |TyCtxtAt { tcx, .. }, def_id| is_eligible_for_coverage(tcx, def_id); + providers.queries.coverage_ids_info = coverage_ids_info; +} + +/// Hook implementation for [`TyCtxt::is_eligible_for_coverage`]. +fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { + // Only instrument functions, methods, and closures (not constants since they are evaluated + // at compile time by Miri). + // FIXME(#73156): Handle source code coverage in const eval, but note, if and when const + // expressions get coverage spans, we will probably have to "carve out" space for const + // expressions from coverage spans in enclosing MIR's, like we do for closures. (That might + // be tricky if const expressions have no corresponding statements in the enclosing MIR. + // Closures are carved out by their initial `Assign` statement.) + if !tcx.def_kind(def_id).is_fn_like() { + trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)"); + return false; + } + + // Don't instrument functions with `#[automatically_derived]` on their + // enclosing impl block, on the assumption that most users won't care about + // coverage for derived impls. + if let Some(impl_of) = tcx.impl_of_method(def_id.to_def_id()) + && tcx.is_automatically_derived(impl_of) + { + trace!("InstrumentCoverage skipped for {def_id:?} (automatically derived)"); + return false; + } + + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) { + trace!("InstrumentCoverage skipped for {def_id:?} (`#[coverage(off)]`)"); + return false; + } + + true } /// Query implementation for `coverage_ids_info`. diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index 4260a6f0c6f7..672de1fbe60f 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -13,6 +13,8 @@ pub(super) enum BcbMappingKind { /// Associates an ordinary executable code span with its corresponding BCB. Code(BasicCoverageBlock), + /// Associates a branch span with BCBs for its true and false arms. + Branch { true_bcb: BasicCoverageBlock, false_bcb: BasicCoverageBlock }, } #[derive(Debug)] @@ -66,6 +68,12 @@ pub(super) fn generate_coverage_spans( // Each span produced by the generator represents an ordinary code region. BcbMapping { kind: BcbMappingKind::Code(bcb), span } })); + + mappings.extend(from_mir::extract_branch_mappings( + mir_body, + hir_info.body_span, + basic_coverage_blocks, + )); } if mappings.is_empty() { @@ -80,6 +88,10 @@ pub(super) fn generate_coverage_spans( for &BcbMapping { kind, span: _ } in &mappings { match kind { BcbMappingKind::Code(bcb) => insert(bcb), + BcbMappingKind::Branch { true_bcb, false_bcb } => { + insert(true_bcb); + insert(false_bcb); + } } } 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 099a354f45dd..86097bdcd953 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs @@ -1,7 +1,9 @@ use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; +use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind}; use rustc_middle::mir::{ - self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, + self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_span::{ExpnKind, MacroKind, Span, Symbol}; @@ -9,6 +11,7 @@ use crate::coverage::graph::{ BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB, }; +use crate::coverage::spans::{BcbMapping, BcbMappingKind}; use crate::coverage::ExtractedHirInfo; /// Traverses the MIR body to produce an initial collection of coverage-relevant @@ -179,8 +182,6 @@ fn is_closure_like(statement: &Statement<'_>) -> bool { /// If the MIR `Statement` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_statement_span(statement: &Statement<'_>) -> Option { - use mir::coverage::CoverageKind; - match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. @@ -225,6 +226,11 @@ fn filtered_statement_span(statement: &Statement<'_>) -> Option { 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, + StatementKind::Coverage(box mir::Coverage { // These coverage statements should not exist prior to coverage instrumentation. kind: CoverageKind::CounterIncrement { .. } | CoverageKind::ExpressionUsed { .. } @@ -358,3 +364,51 @@ fn new( Self { span, visible_macro, bcb, is_hole } } } + +pub(super) fn extract_branch_mappings( + mir_body: &mir::Body<'_>, + body_span: Span, + basic_coverage_blocks: &CoverageGraph, +) -> Vec { + let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { + return vec![]; + }; + + let mut block_markers = IndexVec::>::from_elem_n( + None, + branch_info.num_block_markers, + ); + + // 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 + { + block_markers[id] = Some(bb); + } + } + } + + branch_info + .branch_spans + .iter() + .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| { + // For now, ignore any branch span that was introduced by + // expansion. This makes things like assert macros less noisy. + if !raw_span.ctxt().outer_expn_data().is_root() { + return None; + } + let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?; + + let bcb_from_marker = |marker: BlockMarkerId| { + Some(basic_coverage_blocks.bcb_from_bb(block_markers[marker]?)?) + }; + + let true_bcb = bcb_from_marker(true_marker)?; + let false_bcb = bcb_from_marker(false_marker)?; + + Some(BcbMapping { kind: BcbMappingKind::Branch { true_bcb, false_bcb }, span }) + }) + .collect::>() +} diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 0491de782659..455138015221 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -37,8 +37,9 @@ LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK, }; -use rustc_middle::query::Providers; +use rustc_middle::query; use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; +use rustc_middle::util::Providers; use rustc_span::{source_map::Spanned, sym, DUMMY_SP}; use rustc_trait_selection::traits; @@ -124,7 +125,7 @@ pub fn provide(providers: &mut Providers) { ffi_unwind_calls::provide(providers); shim::provide(providers); cross_crate_inline::provide(providers); - *providers = Providers { + providers.queries = query::Providers { mir_keys, mir_const, mir_const_qualif, @@ -139,7 +140,7 @@ pub fn provide(providers: &mut Providers) { mir_inliner_callees: inline::cycle::mir_inliner_callees, promoted_mir, deduced_param_attrs: deduce_param_attrs::deduced_param_attrs, - ..*providers + ..providers.queries }; } diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 33a446eb55a1..2465f9fbfa8b 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -818,13 +818,16 @@ fn visit_rvalue(&mut self, rvalue: &mir::Rvalue<'tcx>, location: Location) { self.super_rvalue(rvalue, location); } - /// This does not walk the constant, as it has been handled entirely here and trying - /// to walk it would attempt to evaluate the `ty::Const` inside, which doesn't necessarily - /// work, as some constants cannot be represented in the type system. + /// This does not walk the MIR of the constant as that is not needed for codegen, all we need is + /// to ensure that the constant evaluates successfully and walk the result. #[instrument(skip(self), level = "debug")] fn visit_constant(&mut self, constant: &mir::ConstOperand<'tcx>, location: Location) { let const_ = self.monomorphize(constant.const_); let param_env = ty::ParamEnv::reveal_all(); + // Evaluate the constant. This makes const eval failure a collection-time error (rather than + // a codegen-time error). rustc stops after collection if there was an error, so this + // ensures codegen never has to worry about failing consts. + // (codegen relies on this and ICEs will happen if this is violated.) let val = match const_.eval(self.tcx, param_env, None) { Ok(v) => v, Err(ErrorHandled::Reported(..)) => return, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 296eb3120ee4..15041b9cd418 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -1112,6 +1112,9 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co let (items, usage_map) = collector::collect_crate_mono_items(tcx, collection_mode); + // If there was an error during collection (e.g. from one of the constants we evaluated), + // then we stop here. This way codegen does not have to worry about failing constants. + // (codegen relies on this and ICEs will happen if this is violated.) tcx.dcx().abort_if_errors(); let (codegen_units, _) = tcx.sess.time("partition_and_assert_distinct_symbols", || { diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index e86c0522b3cd..2a78f47c34f6 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -270,7 +270,8 @@ fn propagate_node(&mut self, node: &Node<'tcx>, search_item: LocalDefId) { | Node::Ctor(..) | Node::Field(_) | Node::Ty(_) - | Node::Crate(_) => {} + | Node::Crate(_) + | Node::AssocOpaqueTy(..) => {} _ => { bug!( "found unexpected node kind in worklist: {} ({:?})", diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index bf1ea2e27093..f6004fed8284 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -23,18 +23,19 @@ // - `check_unused` finally emits the diagnostics based on the data generated // in the last step -use crate::imports::ImportKind; +use crate::imports::{Import, ImportKind}; use crate::module_to_string; use crate::Resolver; -use crate::NameBindingKind; +use crate::{LexicalScopeBinding, NameBindingKind}; use rustc_ast as ast; use rustc_ast::visit::{self, Visitor}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::{pluralize, MultiSpan}; use rustc_hir::def::{DefKind, Res}; -use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES, UNUSED_IMPORTS}; +use rustc_session::lint::builtin::{MACRO_USE_EXTERN_CRATE, UNUSED_EXTERN_CRATES}; +use rustc_session::lint::builtin::{UNUSED_IMPORTS, UNUSED_QUALIFICATIONS}; use rustc_session::lint::BuiltinLintDiag; use rustc_span::symbol::{kw, Ident}; use rustc_span::{Span, DUMMY_SP}; @@ -514,8 +515,59 @@ pub(crate) fn check_unused(&mut self, krate: &ast::Crate) { } } + let mut redundant_imports = UnordSet::default(); for import in check_redundant_imports { - self.check_for_redundant_imports(import); + if self.check_for_redundant_imports(import) + && let Some(id) = import.id() + { + redundant_imports.insert(id); + } + } + + // The lint fixes for unused_import and unnecessary_qualification may conflict. + // Deleting both unused imports and unnecessary segments of an item may result + // in the item not being found. + for unn_qua in &self.potentially_unnecessary_qualifications { + if let LexicalScopeBinding::Item(name_binding) = unn_qua.binding + && let NameBindingKind::Import { import, .. } = name_binding.kind + && (is_unused_import(import, &unused_imports) + || is_redundant_import(import, &redundant_imports)) + { + continue; + } + + self.lint_buffer.buffer_lint_with_diagnostic( + UNUSED_QUALIFICATIONS, + unn_qua.node_id, + unn_qua.path_span, + "unnecessary qualification", + BuiltinLintDiag::UnusedQualifications { removal_span: unn_qua.removal_span }, + ); + } + + fn is_redundant_import( + import: Import<'_>, + redundant_imports: &UnordSet, + ) -> bool { + if let Some(id) = import.id() + && redundant_imports.contains(&id) + { + return true; + } + false + } + + fn is_unused_import( + import: Import<'_>, + unused_imports: &FxIndexMap, + ) -> bool { + if let Some(unused_import) = unused_imports.get(&import.root_id) + && let Some(id) = import.id() + && unused_import.unused.contains(&id) + { + return true; + } + false } } } diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index ea08041f2aad..9bf3e9ccabdf 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1306,7 +1306,7 @@ fn finalize_import(&mut self, import: Import<'a>) -> Option) { + pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) -> bool { // This function is only called for single imports. let ImportKind::Single { source, target, ref source_bindings, ref target_bindings, id, .. @@ -1317,12 +1317,12 @@ pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) { // Skip if the import is of the form `use source as target` and source != target. if source != target { - return; + return false; } // Skip if the import was produced by a macro. if import.parent_scope.expansion != LocalExpnId::ROOT { - return; + return false; } // Skip if we are inside a named module (in contrast to an anonymous @@ -1332,7 +1332,7 @@ pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) { if import.used.get() == Some(Used::Other) || self.effective_visibilities.is_exported(self.local_def_id(id)) { - return; + return false; } let mut is_redundant = true; @@ -1375,7 +1375,10 @@ pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'a>) { format!("the item `{source}` is imported redundantly"), BuiltinLintDiag::RedundantImport(redundant_spans, source), ); + return true; } + + false } fn resolve_glob_import(&mut self, import: Import<'a>) { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index a996188db022..c9b5c659f0f4 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -580,6 +580,15 @@ fn eval(self, r: &Resolver<'_, '_>) -> bool { } } +/// Used for recording UnnecessaryQualification. +#[derive(Debug)] +pub(crate) struct UnnecessaryQualification<'a> { + pub binding: LexicalScopeBinding<'a>, + pub node_id: NodeId, + pub path_span: Span, + pub removal_span: Span, +} + #[derive(Default)] struct DiagMetadata<'ast> { /// The current trait's associated items' ident, used for diagnostic suggestions. @@ -4654,20 +4663,16 @@ fn lint_unused_qualifications(&mut self, path: &[Segment], ns: Namespace, finali let ns = if i + 1 == path.len() { ns } else { TypeNS }; let res = self.r.partial_res_map.get(&seg.id?)?.full_res()?; let binding = self.resolve_ident_in_lexical_scope(seg.ident, ns, None, None)?; - - (res == binding.res()).then_some(seg) + (res == binding.res()).then_some((seg, binding)) }); - if let Some(unqualified) = unqualified { - self.r.lint_buffer.buffer_lint_with_diagnostic( - lint::builtin::UNUSED_QUALIFICATIONS, - finalize.node_id, - finalize.path_span, - "unnecessary qualification", - lint::BuiltinLintDiag::UnusedQualifications { - removal_span: path[0].ident.span.until(unqualified.ident.span), - }, - ); + if let Some((seg, binding)) = unqualified { + self.r.potentially_unnecessary_qualifications.push(UnnecessaryQualification { + binding, + node_id: finalize.node_id, + path_span: finalize.path_span, + removal_span: path[0].ident.span.until(seg.ident.span), + }); } } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index ccb67ea78cfd..dfc2d029d4cf 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -68,7 +68,7 @@ use diagnostics::{ImportSuggestion, LabelSuggestion, Suggestion}; use imports::{Import, ImportData, ImportKind, NameResolution}; -use late::{HasGenericParams, PathSource, PatternSource}; +use late::{HasGenericParams, PathSource, PatternSource, UnnecessaryQualification}; use macros::{MacroRulesBinding, MacroRulesScope, MacroRulesScopeRef}; use crate::effective_visibilities::EffectiveVisibilitiesVisitor; @@ -372,7 +372,7 @@ fn from(seg: &'a ast::PathSegment) -> Segment { /// This refers to the thing referred by a name. The difference between `Res` and `Item` is that /// items are visible in their whole block, while `Res`es only from the place they are defined /// forward. -#[derive(Debug)] +#[derive(Debug, Copy, Clone)] enum LexicalScopeBinding<'a> { Item(NameBinding<'a>), Res(Res), @@ -1105,6 +1105,8 @@ pub struct Resolver<'a, 'tcx> { potentially_unused_imports: Vec>, + potentially_unnecessary_qualifications: Vec>, + /// Table for mapping struct IDs into struct constructor IDs, /// it's not used during normal resolution, only for better error reporting. /// Also includes of list of each fields visibility @@ -1464,6 +1466,7 @@ pub fn new( local_macro_def_scopes: FxHashMap::default(), name_already_seen: FxHashMap::default(), potentially_unused_imports: Vec::new(), + potentially_unnecessary_qualifications: Default::default(), struct_constructors: Default::default(), unused_macros: Default::default(), unused_macro_rules: Default::default(), diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl index af8c962a2ed6..42c681e49617 100644 --- a/compiler/rustc_session/messages.ftl +++ b/compiler/rustc_session/messages.ftl @@ -111,4 +111,7 @@ session_unleashed_feature_help_unnamed = skipping check that does not even have session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` +session_unsupported_crate_type_for_target = + dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` + session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is greater than 5 diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 5c52ee66128c..b7ee2c980254 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -146,7 +146,7 @@ pub enum InstrumentCoverage { /// Individual flag values controlled by `-Z coverage-options`. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct CoverageOptions { - /// Add branch coverage instrumentation (placeholder flag; not yet implemented). + /// Add branch coverage instrumentation. pub branch: bool, } diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 5f04915a9e75..d523da1ad7e3 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -10,7 +10,7 @@ use rustc_span::{Span, Symbol}; use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTriple}; -use crate::parse::ParseSess; +use crate::{config::CrateType, parse::ParseSess}; pub struct FeatureGateError { pub span: MultiSpan, @@ -345,6 +345,13 @@ pub(crate) struct BinaryFloatLiteralNotSupported { pub span: Span, } +#[derive(Diagnostic)] +#[diag(session_unsupported_crate_type_for_target)] +pub struct UnsupportedCrateTypeForTarget<'a> { + pub crate_type: CrateType, + pub target_triple: &'a TargetTriple, +} + pub fn report_lit_error( psess: &ParseSess, err: LitError, diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index 74d26237f246..35cd3cbab66c 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -1,7 +1,7 @@ //! Related to out filenames of compilation (e.g. binaries). -use crate::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType}; +use crate::config::{self, CrateType, Input, OutFileName, OutputFilenames, OutputType}; use crate::errors::{ - CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, + self, CrateNameDoesNotMatch, CrateNameEmpty, CrateNameInvalid, FileIsNotWriteable, InvalidCharacterInCrateName, InvalidCrateNameHelp, }; use crate::Session; @@ -200,3 +200,64 @@ pub fn invalid_output_for_target(sess: &Session, crate_type: CrateType) -> bool false } + +pub const CRATE_TYPES: &[(Symbol, CrateType)] = &[ + (sym::rlib, CrateType::Rlib), + (sym::dylib, CrateType::Dylib), + (sym::cdylib, CrateType::Cdylib), + (sym::lib, config::default_lib_output()), + (sym::staticlib, CrateType::Staticlib), + (sym::proc_dash_macro, CrateType::ProcMacro), + (sym::bin, CrateType::Executable), +]; + +pub fn categorize_crate_type(s: Symbol) -> Option { + Some(CRATE_TYPES.iter().find(|(key, _)| *key == s)?.1) +} + +pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec { + // If we're generating a test executable, then ignore all other output + // styles at all other locations + if session.opts.test { + return vec![CrateType::Executable]; + } + + // Only check command line flags if present. If no types are specified by + // command line, then reuse the empty `base` Vec to hold the types that + // will be found in crate attributes. + // JUSTIFICATION: before wrapper fn is available + #[allow(rustc::bad_opt_access)] + let mut base = session.opts.crate_types.clone(); + if base.is_empty() { + let attr_types = attrs.iter().filter_map(|a| { + if a.has_name(sym::crate_type) + && let Some(s) = a.value_str() + { + categorize_crate_type(s) + } else { + None + } + }); + base.extend(attr_types); + if base.is_empty() { + base.push(default_output_for_target(session)); + } else { + base.sort(); + base.dedup(); + } + } + + base.retain(|crate_type| { + if invalid_output_for_target(session, *crate_type) { + session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget { + crate_type: *crate_type, + target_triple: &session.opts.target_triple, + }); + false + } else { + true + } + }); + + base +} diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 8488777eda99..509f0def2568 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -23,7 +23,8 @@ use stable_mir::target::{MachineInfo, MachineSize}; use stable_mir::ty::{ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, - ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef, + ForeignItemKind, GenericArgs, LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, UintTy, + VariantDef, }; use stable_mir::{Crate, CrateDef, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol}; use std::cell::RefCell; @@ -341,15 +342,56 @@ fn eval_target_usize(&self, cnst: &Const) -> Result { .ok_or_else(|| Error::new(format!("Const `{cnst:?}` cannot be encoded as u64"))) } - fn usize_to_const(&self, val: u64) -> Result { + fn try_new_const_zst(&self, ty: Ty) -> Result { let mut tables = self.0.borrow_mut(); - let ty = tables.tcx.types.usize; + let tcx = tables.tcx; + let ty_internal = ty.internal(&mut *tables, tcx); + let size = tables + .tcx + .layout_of(ParamEnv::empty().and(ty_internal)) + .map_err(|err| { + Error::new(format!( + "Cannot create a zero-sized constant for type `{ty_internal}`: {err}" + )) + })? + .size; + if size.bytes() != 0 { + return Err(Error::new(format!( + "Cannot create a zero-sized constant for type `{ty_internal}`: \ + Type `{ty_internal}` has {} bytes", + size.bytes() + ))); + } + + Ok(ty::Const::zero_sized(tables.tcx, ty_internal).stable(&mut *tables)) + } + + fn new_const_str(&self, value: &str) -> Const { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let ty = ty::Ty::new_static_str(tcx); + let bytes = value.as_bytes(); + let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes); + + ty::Const::new_value(tcx, val_tree, ty).stable(&mut *tables) + } + + fn new_const_bool(&self, value: bool) -> Const { + let mut tables = self.0.borrow_mut(); + ty::Const::from_bool(tables.tcx, value).stable(&mut *tables) + } + + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result { + let mut tables = self.0.borrow_mut(); + let tcx = tables.tcx; + let ty = ty::Ty::new_uint(tcx, uint_ty.internal(&mut *tables, tcx)); let size = tables.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap().size; - let scalar = ScalarInt::try_from_uint(val, size).ok_or_else(|| { - Error::new(format!("Value overflow: cannot convert `{val}` to usize.")) + // We don't use Const::from_bits since it doesn't have any error checking. + let scalar = ScalarInt::try_from_uint(value, size).ok_or_else(|| { + Error::new(format!("Value overflow: cannot convert `{value}` to `{ty}`.")) })?; - Ok(rustc_middle::ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty) + Ok(ty::Const::new_value(tables.tcx, ValTree::from_scalar_int(scalar), ty) .stable(&mut *tables)) } @@ -556,7 +598,9 @@ fn vtable_allocation( global_alloc: &GlobalAlloc, ) -> Option { let mut tables = self.0.borrow_mut(); - let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else { return None }; + let GlobalAlloc::VTable(ty, trait_ref) = global_alloc else { + return None; + }; let tcx = tables.tcx; let alloc_id = tables.tcx.vtable_allocation(( ty.internal(&mut *tables, tcx), diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs index cb587e28a6c9..486afc5f8f30 100644 --- a/compiler/rustc_target/src/abi/call/mod.rs +++ b/compiler/rustc_target/src/abi/call/mod.rs @@ -633,10 +633,8 @@ pub fn make_indirect(&mut self) { /// If the resulting alignment differs from the type's alignment, /// the argument will be copied to an alloca with sufficient alignment, /// either in the caller (if the type's alignment is lower than the byval alignment) - /// or in the callee† (if the type's alignment is higher than the byval alignment), + /// or in the callee (if the type's alignment is higher than the byval alignment), /// to ensure that Rust code never sees an underaligned pointer. - /// - /// † This is currently broken, see . pub fn make_indirect_byval(&mut self, byval_align: Option) { assert!(!self.layout.is_unsized(), "used byval ABI for unsized layout"); self.make_indirect(); diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs index af533d8db714..2bfb86b592b0 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs @@ -120,6 +120,8 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { + // impl Sized for u*, i*, bool, f*, FnDef, FnPtr, *(const/mut) T, char, &mut? T, [T; N], dyn* Trait, ! + // impl Sized for Coroutine, CoroutineWitness, Closure, CoroutineClosure ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) @@ -152,8 +154,10 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<'tcx>( bug!("unexpected type `{ty}`") } + // impl Sized for (T1, T2, .., Tn) where T1: Sized, T2: Sized, .. Tn: Sized ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + // impl Sized for Adt where T: Sized forall T in field types ty::Adt(def, args) => { let sized_crit = def.sized_constraint(ecx.tcx()); Ok(sized_crit.iter_instantiated(ecx.tcx(), args).map(ty::Binder::dummy).collect()) @@ -167,6 +171,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( ty: Ty<'tcx>, ) -> Result>>, NoSolution> { match *ty.kind() { + // impl Copy/Clone for FnDef, FnPtr ty::FnDef(..) | ty::FnPtr(_) | ty::Error(_) => Ok(vec![]), // Implementations are provided in core @@ -196,12 +201,16 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( bug!("unexpected type `{ty}`") } + // impl Copy/Clone for (T1, T2, .., Tn) where T1: Copy/Clone, T2: Copy/Clone, .. Tn: Copy/Clone ty::Tuple(tys) => Ok(tys.iter().map(ty::Binder::dummy).collect()), + // impl Copy/Clone for Closure where Self::TupledUpvars: Copy/Clone ty::Closure(_, args) => Ok(vec![ty::Binder::dummy(args.as_closure().tupled_upvars_ty())]), ty::CoroutineClosure(..) => Err(NoSolution), + // only when `coroutine_clone` is enabled and the coroutine is movable + // impl Copy/Clone for Coroutine where T: Copy/Clone forall T in (upvars, witnesses) ty::Coroutine(def_id, args) => match ecx.tcx().coroutine_movability(def_id) { Movability::Static => Err(NoSolution), Movability::Movable => { @@ -217,6 +226,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>( } }, + // impl Copy/Clone for CoroutineWitness where T: Copy/Clone forall T in coroutine_hidden_types ty::CoroutineWitness(def_id, args) => Ok(ecx .tcx() .coroutine_hidden_types(def_id) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 281f5cc5685d..c252ad76dfe1 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -250,6 +250,7 @@ fn consider_builtin_fn_ptr_trait_candidate( ) -> QueryResult<'tcx> { let self_ty = goal.predicate.self_ty(); match goal.predicate.polarity { + // impl FnPtr for FnPtr {} ty::ImplPolarity::Positive => { if self_ty.is_fn_ptr() { ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -257,6 +258,7 @@ fn consider_builtin_fn_ptr_trait_candidate( Err(NoSolution) } } + // impl !FnPtr for T where T != FnPtr && T is rigid {} ty::ImplPolarity::Negative => { // If a type is rigid and not a fn ptr, then we know for certain // that it does *not* implement `FnPtr`. @@ -374,6 +376,12 @@ fn consider_builtin_async_fn_kind_helper_candidate( } } + /// ```rust, ignore (not valid rust syntax) + /// impl Tuple for () {} + /// impl Tuple for (T1,) {} + /// impl Tuple for (T1, T2) {} + /// impl Tuple for (T1, .., Tn) {} + /// ``` fn consider_builtin_tuple_candidate( ecx: &mut EvalCtxt<'_, 'tcx>, goal: Goal<'tcx, Self>, 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 d18acb8c864b..b85a05c774fa 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 @@ -3409,6 +3409,8 @@ fn report_opaque_type_auto_trait_leakage( self.dcx().try_steal_replace_and_emit_err(self.tcx.def_span(def_id), StashKey::Cycle, err) } + // FIXME(@lcnr): This function could be changed to trait `TraitRef` directly + // instead of using a `Binder`. fn report_signature_mismatch_error( &self, obligation: &PredicateObligation<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 89654ed61aeb..49091e53be71 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -165,7 +165,6 @@ fn assemble_candidates_from_projected_tys( let poly_trait_predicate = self.infcx.resolve_vars_if_possible(obligation.predicate); let placeholder_trait_predicate = self.infcx.enter_forall_and_leak_universe(poly_trait_predicate); - debug!(?placeholder_trait_predicate); // The bounds returned by `item_bounds` may contain duplicates after // normalization, so try to deduplicate when possible to avoid @@ -184,8 +183,8 @@ fn assemble_candidates_from_projected_tys( selcx.infcx.probe(|_| { match selcx.match_normalize_trait_ref( obligation, - bound.to_poly_trait_ref(), placeholder_trait_predicate.trait_ref, + bound.to_poly_trait_ref(), ) { Ok(None) => { candidates.vec.push(ProjectionCandidate(idx)); @@ -881,8 +880,8 @@ fn assemble_candidates_from_object_ty( self.infcx.probe(|_| { self.match_normalize_trait_ref( obligation, - upcast_trait_ref, placeholder_trait_predicate.trait_ref, + upcast_trait_ref, ) .is_ok() }) diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 70f6b240ab7e..51fc223a5d1b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -9,7 +9,7 @@ use rustc_ast::Mutability; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; +use rustc_infer::infer::HigherRankedType; use rustc_infer::infer::{DefineOpaqueTypes, InferOk}; use rustc_middle::traits::{BuiltinImplSource, SignatureMismatchData}; use rustc_middle::ty::{ @@ -161,8 +161,6 @@ fn confirm_projection_candidate( let placeholder_trait_predicate = self.infcx.enter_forall_and_leak_universe(trait_predicate).trait_ref; let placeholder_self_ty = placeholder_trait_predicate.self_ty(); - let placeholder_trait_predicate = ty::Binder::dummy(placeholder_trait_predicate); - let candidate_predicate = self .for_each_item_bound( placeholder_self_ty, @@ -182,6 +180,11 @@ fn confirm_projection_candidate( .expect("projection candidate is not a trait predicate") .map_bound(|t| t.trait_ref); + let candidate = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + candidate, + ); let mut obligations = Vec::new(); let candidate = normalize_with_depth_to( self, @@ -195,7 +198,7 @@ fn confirm_projection_candidate( obligations.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) + .eq(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented)?, ); @@ -499,7 +502,6 @@ fn confirm_object_candidate( let trait_predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); let self_ty = self.infcx.shallow_resolve(trait_predicate.self_ty()); - let obligation_trait_ref = ty::Binder::dummy(trait_predicate.trait_ref); let ty::Dynamic(data, ..) = *self_ty.kind() else { span_bug!(obligation.cause.span, "object candidate with non-object"); }; @@ -520,19 +522,24 @@ fn confirm_object_candidate( let unnormalized_upcast_trait_ref = supertraits.nth(index).expect("supertraits iterator no longer has as many elements"); + let upcast_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + unnormalized_upcast_trait_ref, + ); let upcast_trait_ref = normalize_with_depth_to( self, obligation.param_env, obligation.cause.clone(), obligation.recursion_depth + 1, - unnormalized_upcast_trait_ref, + upcast_trait_ref, &mut nested, ); nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref) + .eq(DefineOpaqueTypes::No, trait_predicate.trait_ref, upcast_trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| Unimplemented)?, ); @@ -1021,7 +1028,13 @@ fn confirm_poly_trait_refs( obligation: &PolyTraitObligation<'tcx>, self_ty_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, SelectionError<'tcx>> { - let obligation_trait_ref = obligation.predicate.to_poly_trait_ref(); + let obligation_trait_ref = + self.infcx.enter_forall_and_leak_universe(obligation.predicate.to_poly_trait_ref()); + let self_ty_trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + self_ty_trait_ref, + ); // Normalize the obligation and expected trait refs together, because why not let Normalized { obligations: nested, value: (obligation_trait_ref, expected_trait_ref) } = ensure_sufficient_stack(|| { @@ -1037,15 +1050,15 @@ fn confirm_poly_trait_refs( // needed to define opaque types for tests/ui/type-alias-impl-trait/assoc-projection-ice.rs self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) + .eq(DefineOpaqueTypes::Yes, obligation_trait_ref, expected_trait_ref) .map(|InferOk { mut obligations, .. }| { obligations.extend(nested); obligations }) .map_err(|terr| { SignatureMismatch(Box::new(SignatureMismatchData { - expected_trait_ref: obligation_trait_ref, - found_trait_ref: expected_trait_ref, + expected_trait_ref: ty::Binder::dummy(obligation_trait_ref), + found_trait_ref: ty::Binder::dummy(expected_trait_ref), terr, })) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index a6bd1ba9c3f8..d10fe6a94901 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -33,6 +33,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::BoundRegionConversionTime; +use rustc_infer::infer::BoundRegionConversionTime::HigherRankedType; use rustc_infer::infer::DefineOpaqueTypes; use rustc_infer::traits::TraitObligation; use rustc_middle::dep_graph::dep_kinds; @@ -42,7 +43,7 @@ use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::relate::TypeRelation; use rustc_middle::ty::GenericArgsRef; -use rustc_middle::ty::{self, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate}; +use rustc_middle::ty::{self, PolyProjectionPredicate, ToPredicate}; use rustc_middle::ty::{Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_span::symbol::sym; use rustc_span::Symbol; @@ -1651,15 +1652,20 @@ pub(super) fn for_each_item_bound( fn match_normalize_trait_ref( &mut self, obligation: &PolyTraitObligation<'tcx>, - trait_bound: ty::PolyTraitRef<'tcx>, placeholder_trait_ref: ty::TraitRef<'tcx>, - ) -> Result>, ()> { + trait_bound: ty::PolyTraitRef<'tcx>, + ) -> Result>, ()> { debug_assert!(!placeholder_trait_ref.has_escaping_bound_vars()); if placeholder_trait_ref.def_id != trait_bound.def_id() { // Avoid unnecessary normalization return Err(()); } + let trait_bound = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + trait_bound, + ); let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| { normalize_with_depth( self, @@ -1671,7 +1677,7 @@ fn match_normalize_trait_ref( }); self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, ty::Binder::dummy(placeholder_trait_ref), trait_bound) + .eq(DefineOpaqueTypes::No, placeholder_trait_ref, trait_bound) .map(|InferOk { obligations: _, value: () }| { // This method is called within a probe, so we can't have // inference variables and placeholders escape. @@ -1683,7 +1689,6 @@ fn match_normalize_trait_ref( }) .map_err(|_| ()) } - fn where_clause_may_apply<'o>( &mut self, stack: &TraitObligationStack<'o, 'tcx>, @@ -1733,7 +1738,7 @@ pub(super) fn match_projection_projections( let is_match = self .infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation.predicate, infer_projection) + .eq(DefineOpaqueTypes::No, obligation.predicate, infer_projection) .is_ok_and(|InferOk { obligations, value: () }| { self.evaluate_predicates_recursively( TraitObligationStackList::empty(&ProvisionalEvaluationCache::default()), @@ -2533,7 +2538,7 @@ fn match_upcast_principal( nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup( + .eq( DefineOpaqueTypes::No, upcast_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) @@ -2571,7 +2576,7 @@ fn match_upcast_principal( nested.extend( self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .eq(DefineOpaqueTypes::No, source_projection, target_projection) .map_err(|_| SelectionError::Unimplemented)? .into_obligations(), ); @@ -2615,9 +2620,15 @@ fn match_poly_trait_ref( obligation: &PolyTraitObligation<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tcx>, ) -> Result>, ()> { + let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate); + let trait_ref = self.infcx.instantiate_binder_with_fresh_vars( + obligation.cause.span, + HigherRankedType, + poly_trait_ref, + ); self.infcx .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, obligation.predicate.to_poly_trait_ref(), poly_trait_ref) + .eq(DefineOpaqueTypes::No, predicate.trait_ref, trait_ref) .map(|InferOk { obligations, .. }| obligations) .map_err(|_| ()) } diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs index 3c0316fce171..46a68508753c 100644 --- a/compiler/rustc_trait_selection/src/traits/vtable.rs +++ b/compiler/rustc_trait_selection/src/traits/vtable.rs @@ -320,6 +320,7 @@ fn vtable_entries<'tcx>( } /// Find slot base for trait methods within vtable entries of another trait +// FIXME(@lcnr): This isn't a query, so why does it take a tuple as its argument. pub(super) fn vtable_trait_first_method_offset<'tcx>( tcx: TyCtxt<'tcx>, key: ( diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs index 26d3370469a5..4bcbf1c03749 100644 --- a/compiler/rustc_ty_utils/src/assoc.rs +++ b/compiler/rustc_ty_utils/src/assoc.rs @@ -1,10 +1,11 @@ use rustc_data_structures::fx::FxIndexSet; -use rustc_hir as hir; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{self as hir, HirId}; +use rustc_index::IndexVec; use rustc_middle::query::Providers; -use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt}; +use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt, TyCtxtFeed}; use rustc_span::symbol::kw; pub(crate) fn provide(providers: &mut Providers) { @@ -237,6 +238,28 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { } } +fn feed_hir(feed: &TyCtxtFeed<'_, LocalDefId>) { + feed.local_def_id_to_hir_id(HirId::make_owner(feed.def_id())); + + let node = hir::OwnerNode::AssocOpaqueTy(&hir::AssocOpaqueTy {}); + let bodies = Default::default(); + let attrs = hir::AttributeMap::EMPTY; + + let (opt_hash_including_bodies, _) = feed.tcx.hash_owner_nodes(node, &bodies, &attrs.map); + feed.opt_hir_owner_nodes(Some(feed.tcx.arena.alloc(hir::OwnerNodes { + opt_hash_including_bodies, + nodes: IndexVec::from_elem_n( + hir::ParentedNode { + parent: hir::ItemLocalId::INVALID, + node: hir::Node::AssocOpaqueTy(&hir::AssocOpaqueTy {}), + }, + 1, + ), + bodies, + }))); + feed.feed_owner_id().hir_attrs(attrs); +} + /// Given an `opaque_ty_def_id` corresponding to an `impl Trait` in an associated /// function from a trait, synthesize an associated type for that `impl Trait` /// that inherits properties that we infer from the method and the opaque type. @@ -258,9 +281,7 @@ fn associated_type_for_impl_trait_in_trait( let local_def_id = trait_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - // There's no HIR associated with this new synthesized `def_id`, so feed - // `opt_local_def_id_to_hir_id` with `None`. - trait_assoc_ty.opt_local_def_id_to_hir_id(None); + feed_hir(&trait_assoc_ty); // Copy span of the opaque. trait_assoc_ty.def_ident_span(Some(span)); @@ -318,9 +339,7 @@ fn associated_type_for_impl_trait_in_impl( let local_def_id = impl_assoc_ty.def_id(); let def_id = local_def_id.to_def_id(); - // There's no HIR associated with this new synthesized `def_id`, so feed - // `opt_local_def_id_to_hir_id` with `None`. - impl_assoc_ty.opt_local_def_id_to_hir_id(None); + feed_hir(&impl_assoc_ty); // Copy span of the opaque. impl_assoc_ty.def_ident_span(Some(span)); diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs index 1c51c175d81b..f53dbcfbd966 100644 --- a/compiler/stable_mir/src/compiler_interface.rs +++ b/compiler/stable_mir/src/compiler_interface.rs @@ -14,7 +14,7 @@ AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, ForeignDef, ForeignItemKind, ForeignModule, ForeignModuleDef, GenericArgs, GenericPredicates, Generics, ImplDef, ImplTrait, LineInfo, PolyFnSig, RigidTy, Span, TraitDecl, TraitDef, Ty, TyKind, - VariantDef, + UintTy, VariantDef, }; use crate::{ mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind, @@ -101,8 +101,17 @@ pub trait Context { /// Evaluate constant as a target usize. fn eval_target_usize(&self, cnst: &Const) -> Result; - /// Create a target usize constant for the given value. - fn usize_to_const(&self, val: u64) -> Result; + /// Create a new zero-sized constant. + fn try_new_const_zst(&self, ty: Ty) -> Result; + + /// Create a new constant that represents the given string value. + fn new_const_str(&self, value: &str) -> Const; + + /// Create a new constant that represents the given boolean value. + fn new_const_bool(&self, value: bool) -> Const; + + /// Create a new constant that represents the given value. + fn try_new_const_uint(&self, value: u128, uint_ty: UintTy) -> Result; /// Create a new type from the given kind. fn new_rigid_ty(&self, kind: RigidTy) -> Ty; @@ -200,7 +209,7 @@ fn resolve_closure( // A thread local variable that stores a pointer to the tables mapping between TyCtxt // datastructures and stable MIR datastructures -scoped_thread_local! (static TLV: Cell<*const ()>); +scoped_thread_local!(static TLV: Cell<*const ()>); pub fn run(context: &dyn Context, f: F) -> Result where diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs index 86cc748eaec9..a33767520287 100644 --- a/compiler/stable_mir/src/ty.rs +++ b/compiler/stable_mir/src/ty.rs @@ -128,13 +128,38 @@ pub fn ty(&self) -> Ty { /// Creates an interned usize constant. fn try_from_target_usize(val: u64) -> Result { - with(|cx| cx.usize_to_const(val)) + with(|cx| cx.try_new_const_uint(val.into(), UintTy::Usize)) } /// Try to evaluate to a target `usize`. pub fn eval_target_usize(&self) -> Result { with(|cx| cx.eval_target_usize(self)) } + + /// Create a constant that represents a new zero-sized constant of type T. + /// Fails if the type is not a ZST or if it doesn't have a known size. + pub fn try_new_zero_sized(ty: Ty) -> Result { + with(|cx| cx.try_new_const_zst(ty)) + } + + /// Build a new constant that represents the given string. + /// + /// Note that there is no guarantee today about duplication of the same constant. + /// I.e.: Calling this function multiple times with the same argument may or may not return + /// the same allocation. + pub fn from_str(value: &str) -> Const { + with(|cx| cx.new_const_str(value)) + } + + /// Build a new constant that represents the given boolean value. + pub fn from_bool(value: bool) -> Const { + with(|cx| cx.new_const_bool(value)) + } + + /// Build a new constant that represents the given unsigned integer. + pub fn try_from_uint(value: u128, uint_ty: UintTy) -> Result { + with(|cx| cx.try_new_const_uint(value, uint_ty)) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/library/backtrace b/library/backtrace index ddf1b89b861d..6fa4b85b9962 160000 --- a/library/backtrace +++ b/library/backtrace @@ -1 +1 @@ -Subproject commit ddf1b89b861d297c6ef3f09b70d853e81ccc85ff +Subproject commit 6fa4b85b9962c3e1be8c2e5cc605cd078134152b diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 111fb83088b8..30debbffec1a 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -5,8 +5,11 @@ use crate::ffi::c_char; use crate::fmt; use crate::intrinsics; +use crate::iter::FusedIterator; +use crate::marker::PhantomData; use crate::ops; use crate::ptr::addr_of; +use crate::ptr::NonNull; use crate::slice; use crate::slice::memchr; use crate::str; @@ -504,6 +507,13 @@ pub const fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() } + /// We could eventually expose this publicly, if we wanted. + #[inline] + #[must_use] + const fn as_non_null_ptr(&self) -> NonNull { + NonNull::from(&self.inner).as_non_null_ptr() + } + /// Returns the length of `self`. Like C's `strlen`, this does not include the nul terminator. /// /// > **Note**: This method is currently implemented as a constant-time @@ -617,6 +627,26 @@ pub const fn to_bytes_with_nul(&self) -> &[u8] { unsafe { &*(addr_of!(self.inner) as *const [u8]) } } + /// Iterates over the bytes in this C string. + /// + /// The returned iterator will **not** contain the trailing nul terminator + /// that this C string has. + /// + /// # Examples + /// + /// ``` + /// #![feature(cstr_bytes)] + /// use std::ffi::CStr; + /// + /// let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed"); + /// assert!(cstr.bytes().eq(*b"foo")); + /// ``` + #[inline] + #[unstable(feature = "cstr_bytes", issue = "112115")] + pub fn bytes(&self) -> Bytes<'_> { + Bytes::new(self) + } + /// Yields a &[str] slice if the `CStr` contains valid UTF-8. /// /// If the contents of the `CStr` are valid UTF-8 data, this @@ -735,3 +765,64 @@ fn strlen_rt(s: *const c_char) -> usize { intrinsics::const_eval_select((ptr,), strlen_ct, strlen_rt) } } + +/// An iterator over the bytes of a [`CStr`], without the nul terminator. +/// +/// This struct is created by the [`bytes`] method on [`CStr`]. +/// See its documentation for more. +/// +/// [`bytes`]: CStr::bytes +#[must_use = "iterators are lazy and do nothing unless consumed"] +#[unstable(feature = "cstr_bytes", issue = "112115")] +#[derive(Clone, Debug)] +pub struct Bytes<'a> { + // since we know the string is nul-terminated, we only need one pointer + ptr: NonNull, + phantom: PhantomData<&'a u8>, +} +impl<'a> Bytes<'a> { + #[inline] + fn new(s: &'a CStr) -> Self { + Self { ptr: s.as_non_null_ptr().cast(), phantom: PhantomData } + } + + #[inline] + fn is_empty(&self) -> bool { + // SAFETY: We uphold that the pointer is always valid to dereference + // by starting with a valid C string and then never incrementing beyond + // the nul terminator. + unsafe { self.ptr.read() == 0 } + } +} + +#[unstable(feature = "cstr_bytes", issue = "112115")] +impl Iterator for Bytes<'_> { + type Item = u8; + + #[inline] + fn next(&mut self) -> Option { + // SAFETY: We only choose a pointer from a valid C string, which must + // be non-null and contain at least one value. Since we always stop at + // the nul terminator, which is guaranteed to exist, we can assume that + // the pointer is non-null and valid. This lets us safely dereference + // it and assume that adding 1 will create a new, non-null, valid + // pointer. + unsafe { + let ret = self.ptr.read(); + if ret == 0 { + None + } else { + self.ptr = self.ptr.offset(1); + Some(ret) + } + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + if self.is_empty() { (0, Some(0)) } else { (1, None) } + } +} + +#[unstable(feature = "cstr_bytes", issue = "112115")] +impl FusedIterator for Bytes<'_> {} diff --git a/library/core/src/iter/range.rs b/library/core/src/iter/range.rs index 68937161e046..055ead117ea2 100644 --- a/library/core/src/iter/range.rs +++ b/library/core/src/iter/range.rs @@ -22,7 +22,7 @@ unsafe impl TrustedStep for $type {} /// /// The *successor* operation moves towards values that compare greater. /// The *predecessor* operation moves towards values that compare lesser. -#[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] +#[unstable(feature = "step_trait", issue = "42168")] pub trait Step: Clone + PartialOrd + Sized { /// Returns the number of *successor* steps required to get from `start` to `end`. /// @@ -52,15 +52,12 @@ pub trait Step: Clone + PartialOrd + Sized { /// For any `a`, `n`, and `m`: /// /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))` - /// - /// For any `a`, `n`, and `m` where `n + m` does not overflow: - /// - /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)` + /// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == try { Step::forward_checked(a, n.checked_add(m)) }` /// /// For any `a` and `n`: /// /// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))` - /// * Corollary: `Step::forward_checked(&a, 0) == Some(a)` + /// * Corollary: `Step::forward_checked(a, 0) == Some(a)` fn forward_checked(start: Self, count: usize) -> Option; /// Returns the value that would be obtained by taking the *successor* @@ -106,6 +103,7 @@ fn forward(start: Self, count: usize) -> Self { /// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)` /// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`, /// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`. + /// * Corollary: `Step::forward_unchecked(a, 0)` is always safe. /// /// For any `a` and `n`, where no overflow occurs: /// @@ -128,8 +126,8 @@ unsafe fn forward_unchecked(start: Self, count: usize) -> Self { /// /// For any `a` and `n`: /// - /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))` - /// * Corollary: `Step::backward_checked(&a, 0) == Some(a)` + /// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(x, 1))` + /// * Corollary: `Step::backward_checked(a, 0) == Some(a)` fn backward_checked(start: Self, count: usize) -> Option; /// Returns the value that would be obtained by taking the *predecessor* @@ -175,6 +173,7 @@ fn backward(start: Self, count: usize) -> Self { /// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)` /// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`, /// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`. + /// * Corollary: `Step::backward_unchecked(a, 0)` is always safe. /// /// For any `a` and `n`, where no overflow occurs: /// @@ -184,8 +183,25 @@ unsafe fn backward_unchecked(start: Self, count: usize) -> Self { } } -// These are still macro-generated because the integer literals resolve to different types. -macro_rules! step_identical_methods { +// Separate impls for signed ranges because the distance within a signed range can be larger +// than the signed::MAX value. Therefore `as` casting to the signed type would be incorrect. +macro_rules! step_signed_methods { + ($unsigned: ty) => { + #[inline] + unsafe fn forward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start + n` doesn't overflow. + unsafe { start.checked_add_unsigned(n as $unsigned).unwrap_unchecked() } + } + + #[inline] + unsafe fn backward_unchecked(start: Self, n: usize) -> Self { + // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. + unsafe { start.checked_sub_unsigned(n as $unsigned).unwrap_unchecked() } + } + }; +} + +macro_rules! step_unsigned_methods { () => { #[inline] unsafe fn forward_unchecked(start: Self, n: usize) -> Self { @@ -198,7 +214,12 @@ unsafe fn backward_unchecked(start: Self, n: usize) -> Self { // SAFETY: the caller has to guarantee that `start - n` doesn't overflow. unsafe { start.unchecked_sub(n as Self) } } + }; +} +// These are still macro-generated because the integer literals resolve to different types. +macro_rules! step_identical_methods { + () => { #[inline] #[allow(arithmetic_overflow)] #[rustc_inherit_overflow_checks] @@ -239,6 +260,7 @@ macro_rules! step_integer_impls { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_narrower { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -271,6 +293,7 @@ fn backward_checked(start: Self, n: usize) -> Option { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_narrower { step_identical_methods!(); + step_signed_methods!($u_narrower); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -335,6 +358,7 @@ fn backward_checked(start: Self, n: usize) -> Option { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $u_wider { step_identical_methods!(); + step_unsigned_methods!(); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { @@ -360,6 +384,7 @@ fn backward_checked(start: Self, n: usize) -> Option { #[unstable(feature = "step_trait", reason = "recently redesigned", issue = "42168")] impl Step for $i_wider { step_identical_methods!(); + step_signed_methods!($u_wider); #[inline] fn steps_between(start: &Self, end: &Self) -> Option { diff --git a/library/core/src/time.rs b/library/core/src/time.rs index b533f5399387..78494b866b10 100644 --- a/library/core/src/time.rs +++ b/library/core/src/time.rs @@ -856,6 +856,48 @@ pub const fn as_secs_f32(&self) -> f32 { (self.secs as f32) + (self.nanos.0 as f32) / (NANOS_PER_SEC as f32) } + /// Returns the number of milliseconds contained by this `Duration` as `f64`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// #![feature(duration_millis_float)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 345_678_000); + /// assert_eq!(dur.as_millis_f64(), 2345.678); + /// ``` + #[unstable(feature = "duration_millis_float", issue = "122451")] + #[must_use] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] + pub const fn as_millis_f64(&self) -> f64 { + (self.secs as f64) * (MILLIS_PER_SEC as f64) + + (self.nanos.0 as f64) / (NANOS_PER_MILLI as f64) + } + + /// Returns the number of milliseconds contained by this `Duration` as `f32`. + /// + /// The returned value does include the fractional (nanosecond) part of the duration. + /// + /// # Examples + /// ``` + /// #![feature(duration_millis_float)] + /// use std::time::Duration; + /// + /// let dur = Duration::new(2, 345_678_000); + /// assert_eq!(dur.as_millis_f32(), 2345.678); + /// ``` + #[unstable(feature = "duration_millis_float", issue = "122451")] + #[must_use] + #[inline] + #[rustc_const_unstable(feature = "duration_consts_float", issue = "72440")] + pub const fn as_millis_f32(&self) -> f32 { + (self.secs as f32) * (MILLIS_PER_SEC as f32) + + (self.nanos.0 as f32) / (NANOS_PER_MILLI as f32) + } + /// Creates a new `Duration` from the specified number of seconds represented /// as `f64`. /// diff --git a/library/core/tests/iter/range.rs b/library/core/tests/iter/range.rs index 9af07119a89a..e31db0732e09 100644 --- a/library/core/tests/iter/range.rs +++ b/library/core/tests/iter/range.rs @@ -325,6 +325,11 @@ fn test_range_advance_by() { assert_eq!(Ok(()), r.advance_back_by(usize::MAX)); assert_eq!((r.start, r.end), (0u128 + usize::MAX as u128, u128::MAX - usize::MAX as u128)); + + // issue 122420, Step::forward_unchecked was unsound for signed integers + let mut r = -128i8..127; + assert_eq!(Ok(()), r.advance_by(200)); + assert_eq!(r.next(), Some(72)); } #[test] diff --git a/library/profiler_builtins/Cargo.toml b/library/profiler_builtins/Cargo.toml index 3371dfa12425..937149f8e86d 100644 --- a/library/profiler_builtins/Cargo.toml +++ b/library/profiler_builtins/Cargo.toml @@ -13,4 +13,4 @@ core = { path = "../core" } compiler_builtins = { version = "0.1.0", features = ['rustc-dep-of-std'] } [build-dependencies] -cc = "1.0.69" +cc = "1.0.90" diff --git a/library/std/src/os/unix/process.rs b/library/std/src/os/unix/process.rs index e45457b2e42b..72ea54bd7720 100644 --- a/library/std/src/os/unix/process.rs +++ b/library/std/src/os/unix/process.rs @@ -39,6 +39,13 @@ pub trait CommandExt: Sealed { /// Sets the child process's user ID. This translates to a /// `setuid` call in the child process. Failure in the `setuid` /// call will cause the spawn to fail. + /// + /// # Notes + /// + /// This will also trigger a call to `setgroups(0, NULL)` in the child + /// process if no groups have been specified. + /// This removes supplementary groups that might have given the child + /// unwanted permissions. #[stable(feature = "rust1", since = "1.0.0")] fn uid(&mut self, id: UserId) -> &mut process::Command; diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index d04804a5f3df..a9d1983dce61 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -7,7 +7,7 @@ use crate::fs; use crate::io; use crate::marker::PhantomData; -use crate::mem::forget; +use crate::mem::{forget, ManuallyDrop}; use crate::ptr; use crate::sys; use crate::sys::cvt; @@ -91,7 +91,7 @@ pub struct OwnedHandle { #[repr(transparent)] #[stable(feature = "io_safety", since = "1.63.0")] #[derive(Debug)] -pub struct HandleOrNull(OwnedHandle); +pub struct HandleOrNull(RawHandle); /// FFI type for handles in return values or out parameters, where `INVALID_HANDLE_VALUE` is used /// as a sentry value to indicate errors, such as in the return value of `CreateFileW`. This uses @@ -110,7 +110,7 @@ pub struct OwnedHandle { #[repr(transparent)] #[stable(feature = "io_safety", since = "1.63.0")] #[derive(Debug)] -pub struct HandleOrInvalid(OwnedHandle); +pub struct HandleOrInvalid(RawHandle); // The Windows [`HANDLE`] type may be transferred across and shared between // thread boundaries (despite containing a `*mut void`, which in general isn't @@ -163,15 +163,24 @@ impl TryFrom for OwnedHandle { #[inline] fn try_from(handle_or_null: HandleOrNull) -> Result { - let owned_handle = handle_or_null.0; - if owned_handle.handle.is_null() { - // Don't call `CloseHandle`; it'd be harmless, except that it could - // overwrite the `GetLastError` error. - forget(owned_handle); - - Err(NullHandleError(())) + let handle_or_null = ManuallyDrop::new(handle_or_null); + if handle_or_null.is_valid() { + // SAFETY: The handle is not null. + Ok(unsafe { OwnedHandle::from_raw_handle(handle_or_null.0) }) } else { - Ok(owned_handle) + Err(NullHandleError(())) + } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl Drop for HandleOrNull { + #[inline] + fn drop(&mut self) { + if self.is_valid() { + unsafe { + let _ = sys::c::CloseHandle(self.0); + } } } } @@ -232,15 +241,24 @@ impl TryFrom for OwnedHandle { #[inline] fn try_from(handle_or_invalid: HandleOrInvalid) -> Result { - let owned_handle = handle_or_invalid.0; - if owned_handle.handle == sys::c::INVALID_HANDLE_VALUE { - // Don't call `CloseHandle`; it'd be harmless, except that it could - // overwrite the `GetLastError` error. - forget(owned_handle); - - Err(InvalidHandleError(())) + let handle_or_invalid = ManuallyDrop::new(handle_or_invalid); + if handle_or_invalid.is_valid() { + // SAFETY: The handle is not invalid. + Ok(unsafe { OwnedHandle::from_raw_handle(handle_or_invalid.0) }) } else { - Ok(owned_handle) + Err(InvalidHandleError(())) + } + } +} + +#[stable(feature = "io_safety", since = "1.63.0")] +impl Drop for HandleOrInvalid { + #[inline] + fn drop(&mut self) { + if self.is_valid() { + unsafe { + let _ = sys::c::CloseHandle(self.0); + } } } } @@ -333,7 +351,11 @@ impl HandleOrNull { #[stable(feature = "io_safety", since = "1.63.0")] #[inline] pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { - Self(OwnedHandle::from_raw_handle(handle)) + Self(handle) + } + + fn is_valid(&self) -> bool { + !self.0.is_null() } } @@ -356,7 +378,11 @@ impl HandleOrInvalid { #[stable(feature = "io_safety", since = "1.63.0")] #[inline] pub unsafe fn from_raw_handle(handle: RawHandle) -> Self { - Self(OwnedHandle::from_raw_handle(handle)) + Self(handle) + } + + fn is_valid(&self) -> bool { + self.0 != sys::c::INVALID_HANDLE_VALUE } } diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index 422a99380cc5..b968f8df34c2 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -463,15 +463,15 @@ pub fn file_type(&self) -> FileType { #[cfg(target_os = "netbsd")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64)) + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtimensec as i64) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64)) + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atimensec as i64) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64)) + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtimensec as i64) } } @@ -503,16 +503,16 @@ pub fn modified(&self) -> io::Result { #[cfg(target_pointer_width = "32")] cfg_has_statx! { if let Some(mtime) = self.stx_mtime() { - return Ok(SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64)); + return SystemTime::new(mtime.tv_sec, mtime.tv_nsec as i64); } } - Ok(SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64)) + SystemTime::new(self.stat.st_mtime as i64, self.stat.st_mtime_nsec as i64) } #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtime as i64, 0)) + SystemTime::new(self.stat.st_mtime as i64, 0) } #[cfg(any(target_os = "horizon", target_os = "hurd"))] @@ -531,16 +531,16 @@ pub fn accessed(&self) -> io::Result { #[cfg(target_pointer_width = "32")] cfg_has_statx! { if let Some(atime) = self.stx_atime() { - return Ok(SystemTime::new(atime.tv_sec, atime.tv_nsec as i64)); + return SystemTime::new(atime.tv_sec, atime.tv_nsec as i64); } } - Ok(SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64)) + SystemTime::new(self.stat.st_atime as i64, self.stat.st_atime_nsec as i64) } #[cfg(any(target_os = "vxworks", target_os = "espidf", target_os = "vita"))] pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atime as i64, 0)) + SystemTime::new(self.stat.st_atime as i64, 0) } #[cfg(any(target_os = "horizon", target_os = "hurd"))] @@ -557,7 +557,7 @@ pub fn accessed(&self) -> io::Result { target_os = "watchos", ))] pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)) + SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64) } #[cfg(not(any( @@ -573,7 +573,7 @@ pub fn created(&self) -> io::Result { cfg_has_statx! { if let Some(ext) = &self.statx_extra_fields { return if (ext.stx_mask & libc::STATX_BTIME) != 0 { - Ok(SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64)) + SystemTime::new(ext.stx_btime.tv_sec, ext.stx_btime.tv_nsec as i64) } else { Err(io::const_io_error!( io::ErrorKind::Unsupported, @@ -592,22 +592,22 @@ pub fn created(&self) -> io::Result { #[cfg(target_os = "vita")] pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctime as i64, 0)) + SystemTime::new(self.stat.st_ctime as i64, 0) } } #[cfg(target_os = "nto")] impl FileAttr { pub fn modified(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec)) + SystemTime::new(self.stat.st_mtim.tv_sec, self.stat.st_mtim.tv_nsec) } pub fn accessed(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec)) + SystemTime::new(self.stat.st_atim.tv_sec, self.stat.st_atim.tv_nsec) } pub fn created(&self) -> io::Result { - Ok(SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec)) + SystemTime::new(self.stat.st_ctim.tv_sec, self.stat.st_ctim.tv_nsec) } } diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index 97cbd1929d32..f017d39d804a 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -330,14 +330,22 @@ unsafe fn do_exec( if let Some(u) = self.get_uid() { // When dropping privileges from root, the `setgroups` call // will remove any extraneous groups. We only drop groups - // if the current uid is 0 and we weren't given an explicit + // if we have CAP_SETGID and we weren't given an explicit // set of groups. If we don't call this, then even though our // uid has dropped, we may still have groups that enable us to // do super-user things. //FIXME: Redox kernel does not support setgroups yet #[cfg(not(target_os = "redox"))] - if libc::getuid() == 0 && self.get_groups().is_none() { - cvt(libc::setgroups(0, crate::ptr::null()))?; + if self.get_groups().is_none() { + let res = cvt(libc::setgroups(0, crate::ptr::null())); + if let Err(e) = res { + // Here we ignore the case of not having CAP_SETGID. + // An alternative would be to require CAP_SETGID (in + // addition to CAP_SETUID) for setting the UID. + if e.raw_os_error() != Some(libc::EPERM) { + return Err(e.into()); + } + } } cvt(libc::setuid(u as uid_t))?; } diff --git a/library/std/src/sys/pal/unix/time.rs b/library/std/src/sys/pal/unix/time.rs index 251a37d54dd8..0440f33ded13 100644 --- a/library/std/src/sys/pal/unix/time.rs +++ b/library/std/src/sys/pal/unix/time.rs @@ -1,5 +1,5 @@ -use crate::fmt; use crate::time::Duration; +use crate::{fmt, io}; const NSEC_PER_SEC: u64 = 1_000_000_000; pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() }; @@ -34,8 +34,8 @@ pub(crate) struct Timespec { impl SystemTime { #[cfg_attr(any(target_os = "horizon", target_os = "hurd"), allow(unused))] - pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime { - SystemTime { t: Timespec::new(tv_sec, tv_nsec) } + pub fn new(tv_sec: i64, tv_nsec: i64) -> Result { + Ok(SystemTime { t: Timespec::new(tv_sec, tv_nsec)? }) } pub fn now() -> SystemTime { @@ -55,12 +55,6 @@ pub fn checked_sub_duration(&self, other: &Duration) -> Option { } } -impl From for SystemTime { - fn from(t: libc::timespec) -> SystemTime { - SystemTime { t: Timespec::from(t) } - } -} - impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SystemTime") @@ -71,11 +65,15 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { } impl Timespec { - pub const fn zero() -> Timespec { - Timespec::new(0, 0) + const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec { + Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } } - const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { + pub const fn zero() -> Timespec { + unsafe { Self::new_unchecked(0, 0) } + } + + const fn new(tv_sec: i64, tv_nsec: i64) -> Result { // On Apple OS, dates before epoch are represented differently than on other // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1` // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and @@ -100,9 +98,11 @@ const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec { } else { (tv_sec, tv_nsec) }; - assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64); - // SAFETY: The assert above checks tv_nsec is within the valid range - Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } } + if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 { + Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) }) + } else { + Err(io::const_io_error!(io::ErrorKind::InvalidData, "Invalid timestamp")) + } } pub fn now(clock: libc::clockid_t) -> Timespec { @@ -126,13 +126,15 @@ pub fn now(clock: libc::clockid_t) -> Timespec { if let Some(clock_gettime64) = __clock_gettime64.get() { let mut t = MaybeUninit::uninit(); cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap(); - return Timespec::from(unsafe { t.assume_init() }); + let t = unsafe { t.assume_init() }; + return Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap(); } } let mut t = MaybeUninit::uninit(); cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap(); - Timespec::from(unsafe { t.assume_init() }) + let t = unsafe { t.assume_init() }; + Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap() } pub fn sub_timespec(&self, other: &Timespec) -> Result { @@ -178,7 +180,7 @@ pub fn checked_add_duration(&self, other: &Duration) -> Option { nsec -= NSEC_PER_SEC as u32; secs = secs.checked_add(1)?; } - Some(Timespec::new(secs, nsec.into())) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } pub fn checked_sub_duration(&self, other: &Duration) -> Option { @@ -190,7 +192,7 @@ pub fn checked_sub_duration(&self, other: &Duration) -> Option { nsec += NSEC_PER_SEC as i32; secs = secs.checked_sub(1)?; } - Some(Timespec::new(secs, nsec.into())) + Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) }) } #[allow(dead_code)] @@ -226,12 +228,6 @@ pub fn to_timespec64(&self) -> __timespec64 { } } -impl From for Timespec { - fn from(t: libc::timespec) -> Timespec { - Timespec::new(t.tv_sec as i64, t.tv_nsec as i64) - } -} - #[cfg(all( target_os = "linux", target_env = "gnu", @@ -260,18 +256,6 @@ pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self { } } -#[cfg(all( - target_os = "linux", - target_env = "gnu", - target_pointer_width = "32", - not(target_arch = "riscv32") -))] -impl From<__timespec64> for Timespec { - fn from(t: __timespec64) -> Timespec { - Timespec::new(t.tv_sec, t.tv_nsec.into()) - } -} - #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Instant { t: Timespec, diff --git a/src/doc/rustc/src/instrument-coverage.md b/src/doc/rustc/src/instrument-coverage.md index 7780f2102ba6..185a3ba5dbd4 100644 --- a/src/doc/rustc/src/instrument-coverage.md +++ b/src/doc/rustc/src/instrument-coverage.md @@ -352,7 +352,7 @@ This unstable option provides finer control over some aspects of coverage instrumentation. Pass one or more of the following values, separated by commas. - `branch` or `no-branch` - - Placeholder for potential branch coverage support in the future. + - Enables or disables branch coverage instrumentation. ## Other references diff --git a/src/doc/rustc/src/platform-support/openharmony.md b/src/doc/rustc/src/platform-support/openharmony.md index 9f90e7413268..b2ddbfdfa291 100644 --- a/src/doc/rustc/src/platform-support/openharmony.md +++ b/src/doc/rustc/src/platform-support/openharmony.md @@ -96,9 +96,34 @@ exec /path/to/ohos-sdk/linux/native/llvm/bin/clang++ \ Future versions of the OpenHarmony SDK will avoid the need for this process. -## Building the target +## Building Rust programs -To build a rust toolchain, create a `config.toml` with the following contents: +Rustup ships pre-compiled artifacts for this target, which you can install with: +```sh +rustup target add aarch64-unknown-linux-ohos +rustup target add armv7-unknown-linux-ohos +rustup target add x86_64-unknown-linux-ohos +``` + +You will need to configure the linker to use in `~/.cargo/config.toml`: +```toml +[target.aarch64-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/aarch64-unknown-linux-ohos-clang.sh" + +[target.armv7-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/armv7-unknown-linux-ohos-clang.sh" + +[target.x86_64-unknown-linux-ohos] +ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" +linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" +``` + +## Building the target from source + +Instead of using `rustup`, you can instead build a rust toolchain from source. +Create a `config.toml` with the following contents: ```toml profile = "compiler" @@ -130,28 +155,6 @@ ranlib = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ranlib" linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" ``` -## Building Rust programs - -Rust does not yet ship pre-compiled artifacts for this target. To compile for -this target, you will either need to build Rust with the target enabled (see -"Building the target" above), or build your own copy of `core` by using -`build-std` or similar. - -You will need to configure the linker to use in `~/.cargo/config`: -```toml -[target.aarch64-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/aarch64-unknown-linux-ohos-clang.sh" - -[target.armv7-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/armv7-unknown-linux-ohos-clang.sh" - -[target.x86_64-unknown-linux-ohos] -ar = "/path/to/ohos-sdk/linux/native/llvm/bin/llvm-ar" -linker = "/path/to/x86_64-unknown-linux-ohos-clang.sh" -``` - ## Testing Running the Rust testsuite is possible, but currently difficult due to the way diff --git a/src/doc/rustdoc/src/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md index b5f4060f0590..e2def14b357e 100644 --- a/src/doc/rustdoc/src/read-documentation/search.md +++ b/src/doc/rustdoc/src/read-documentation/search.md @@ -63,11 +63,12 @@ Before describing the syntax in more detail, here's a few sample searches of the standard library and functions that are included in the results list: | Query | Results | -|-------|--------| +|-------|---------| | [`usize -> vec`][] | `slice::repeat` and `Vec::with_capacity` | | [`vec, vec -> bool`][] | `Vec::eq` | | [`option, fnonce -> option`][] | `Option::map` and `Option::and_then` | -| [`option, fnonce -> option`][] | `Option::filter` and `Option::inspect` | +| [`option, (fnonce (T) -> bool) -> option`][optionfilter] | `Option::filter` | +| [`option, (T -> bool) -> option`][optionfilter2] | `Option::filter` | | [`option -> default`][] | `Option::unwrap_or_default` | | [`stdout, [u8]`][stdoutu8] | `Stdout::write` | | [`any -> !`][] | `panic::panic_any` | @@ -77,7 +78,8 @@ the standard library and functions that are included in the results list: [`usize -> vec`]: ../../std/vec/struct.Vec.html?search=usize%20-%3E%20vec&filter-crate=std [`vec, vec -> bool`]: ../../std/vec/struct.Vec.html?search=vec,%20vec%20-%3E%20bool&filter-crate=std [`option, fnonce -> option`]: ../../std/vec/struct.Vec.html?search=option%2C%20fnonce%20->%20option&filter-crate=std -[`option, fnonce -> option`]: ../../std/vec/struct.Vec.html?search=option%2C%20fnonce%20->%20option&filter-crate=std +[optionfilter]: ../../std/vec/struct.Vec.html?search=option%2C+(fnonce+(T)+->+bool)+->+option&filter-crate=std +[optionfilter2]: ../../std/vec/struct.Vec.html?search=option%2C+(T+->+bool)+->+option&filter-crate=std [`option -> default`]: ../../std/vec/struct.Vec.html?search=option%20-%3E%20default&filter-crate=std [`any -> !`]: ../../std/vec/struct.Vec.html?search=any%20-%3E%20!&filter-crate=std [stdoutu8]: ../../std/vec/struct.Vec.html?search=stdout%2C%20[u8]&filter-crate=std @@ -151,16 +153,26 @@ will match these queries: But it *does not* match `Result` or `Result>`. +To search for a function that accepts a function as a parameter, +like `Iterator::all`, wrap the nested signature in parenthesis, +as in [`Iterator, (T -> bool) -> bool`][iterator-all]. +You can also search for a specific closure trait, +such as `Iterator, (FnMut(T) -> bool) -> bool`, +but you need to know which one you want. + +[iterator-all]: ../../std/vec/struct.Vec.html?search=Iterator%2C+(T+->+bool)+->+bool&filter-crate=std + ### Primitives with Special Syntax -| Shorthand | Explicit names | -| --------- | ------------------------------------------------ | -| `[]` | `primitive:slice` and/or `primitive:array` | -| `[T]` | `primitive:slice` and/or `primitive:array` | -| `()` | `primitive:unit` and/or `primitive:tuple` | -| `(T)` | `T` | -| `(T,)` | `primitive:tuple` | -| `!` | `primitive:never` | +| Shorthand | Explicit names | +| ---------------- | ------------------------------------------------- | +| `[]` | `primitive:slice` and/or `primitive:array` | +| `[T]` | `primitive:slice` and/or `primitive:array` | +| `()` | `primitive:unit` and/or `primitive:tuple` | +| `(T)` | `T` | +| `(T,)` | `primitive:tuple` | +| `!` | `primitive:never` | +| `(T, U -> V, W)` | `fn(T, U) -> (V, W)`, `Fn`, `FnMut`, and `FnOnce` | When searching for `[]`, Rustdoc will return search results with either slices or arrays. If you know which one you want, you can force it to return results @@ -180,6 +192,10 @@ results for types that match tuples, even though it also matches the type on its own. That is, `(u32)` matches `(u32,)` for the exact same reason that it also matches `Result`. +The `->` operator has lower precedence than comma. If it's not wrapped +in brackets, it delimits the return value for the function being searched for. +To search for functions that take functions as parameters, use parenthesis. + ### Limitations and quirks of type-based search Type-based search is still a buggy, experimental, work-in-progress feature. @@ -218,9 +234,6 @@ Most of these limitations should be addressed in future version of Rustdoc. * Searching for lifetimes is not supported. - * It's impossible to search for closures based on their parameters or - return values. - * It's impossible to search based on the length of an array. ## Item filtering @@ -237,19 +250,21 @@ Item filters can be used in both name-based and type signature-based searches. ```text ident = *(ALPHA / DIGIT / "_") -path = ident *(DOUBLE-COLON ident) [!] +path = ident *(DOUBLE-COLON ident) [BANG] slice-like = OPEN-SQUARE-BRACKET [ nonempty-arg-list ] CLOSE-SQUARE-BRACKET tuple-like = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN -arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like / [!]) +arg = [type-filter *WS COLON *WS] (path [generics] / slice-like / tuple-like) type-sep = COMMA/WS *(COMMA/WS) -nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) +nonempty-arg-list = *(type-sep) arg *(type-sep arg) *(type-sep) [ return-args ] generic-arg-list = *(type-sep) arg [ EQUAL arg ] *(type-sep arg [ EQUAL arg ]) *(type-sep) -generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) +normal-generics = OPEN-ANGLE-BRACKET [ generic-arg-list ] *(type-sep) CLOSE-ANGLE-BRACKET +fn-like-generics = OPEN-PAREN [ nonempty-arg-list ] CLOSE-PAREN [ RETURN-ARROW arg ] +generics = normal-generics / fn-like-generics return-args = RETURN-ARROW *(type-sep) nonempty-arg-list exact-search = [type-filter *WS COLON] [ RETURN-ARROW ] *WS QUOTE ident QUOTE [ generics ] -type-search = [ nonempty-arg-list ] [ return-args ] +type-search = [ nonempty-arg-list ] query = *WS (exact-search / type-search) *WS @@ -294,6 +309,7 @@ QUOTE = %x22 COMMA = "," RETURN-ARROW = "->" EQUAL = "=" +BANG = "!" ALPHA = %x41-5A / %x61-7A ; A-Z / a-z DIGIT = %x30-39 diff --git a/src/doc/unstable-book/src/compiler-flags/coverage-options.md b/src/doc/unstable-book/src/compiler-flags/coverage-options.md index 105dce615117..450573cc6c74 100644 --- a/src/doc/unstable-book/src/compiler-flags/coverage-options.md +++ b/src/doc/unstable-book/src/compiler-flags/coverage-options.md @@ -5,4 +5,4 @@ This option controls details of the coverage instrumentation performed by Multiple options can be passed, separated by commas. Valid options are: -- `branch` or `no-branch`: Placeholder for future branch coverage support. +- `branch` or `no-branch`: Enables or disables branch coverage instrumentation. diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs index cb059082f85b..f153a9083291 100644 --- a/src/librustdoc/html/render/search_index.rs +++ b/src/librustdoc/html/render/search_index.rs @@ -4,6 +4,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_middle::ty::TyCtxt; use rustc_span::def_id::DefId; +use rustc_span::sym; use rustc_span::symbol::Symbol; use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer}; use thin_vec::ThinVec; @@ -566,6 +567,7 @@ fn get_index_type_id( // The type parameters are converted to generics in `simplify_fn_type` clean::Slice(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Slice)), clean::Array(_, _) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Array)), + clean::BareFunction(_) => Some(RenderTypeId::Primitive(clean::PrimitiveType::Fn)), clean::Tuple(ref n) if n.is_empty() => { Some(RenderTypeId::Primitive(clean::PrimitiveType::Unit)) } @@ -584,7 +586,7 @@ fn get_index_type_id( } } // Not supported yet - clean::BareFunction(_) | clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, + clean::Generic(_) | clean::ImplTrait(_) | clean::Infer => None, } } @@ -785,6 +787,42 @@ fn simplify_fn_type<'tcx, 'a>( ); } res.push(get_index_type(arg, ty_generics, rgen)); + } else if let Type::BareFunction(ref bf) = *arg { + let mut ty_generics = Vec::new(); + for ty in bf.decl.inputs.values.iter().map(|arg| &arg.type_) { + simplify_fn_type( + self_, + generics, + ty, + tcx, + recurse + 1, + &mut ty_generics, + rgen, + is_return, + cache, + ); + } + // The search index, for simplicity's sake, represents fn pointers and closures + // the same way: as a tuple for the parameters, and an associated type for the + // return type. + let mut ty_output = Vec::new(); + simplify_fn_type( + self_, + generics, + &bf.decl.output, + tcx, + recurse + 1, + &mut ty_output, + rgen, + is_return, + cache, + ); + let ty_bindings = vec![(RenderTypeId::AssociatedType(sym::Output), ty_output)]; + res.push(RenderType { + id: get_index_type_id(&arg, rgen), + bindings: Some(ty_bindings), + generics: Some(ty_generics), + }); } else { // This is not a type parameter. So for example if we have `T, U: Option`, and we're // looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't. diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 7995a33f09f9..875ebe2fc90d 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -1,3 +1,4 @@ +// ignore-tidy-filelength /* global addClass, getNakedUrl, getSettingValue */ /* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi, exports */ @@ -80,6 +81,13 @@ const longItemTypes = [ const TY_GENERIC = itemTypes.indexOf("generic"); const ROOT_PATH = typeof window !== "undefined" ? window.rootPath : "../"; +// Hard limit on how deep to recurse into generics when doing type-driven search. +// This needs limited, partially because +// a search for `Ty` shouldn't match `WithInfcx>>>>`, +// but mostly because this is the simplest and most principled way to limit the number +// of permutations we need to check. +const UNBOXING_LIMIT = 5; + // In the search display, allows to switch between tabs. function printTab(nb) { let iter = 0; @@ -245,33 +253,49 @@ function initSearch(rawSearchIndex) { * * @type {Map} */ - let typeNameIdMap; + const typeNameIdMap = new Map(); const ALIASES = new Map(); /** * Special type name IDs for searching by array. */ - let typeNameIdOfArray; + const typeNameIdOfArray = buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. */ - let typeNameIdOfSlice; + const typeNameIdOfSlice = buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). */ - let typeNameIdOfArrayOrSlice; + const typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. */ - let typeNameIdOfTuple; + const typeNameIdOfTuple = buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. */ - let typeNameIdOfUnit; + const typeNameIdOfUnit = buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). */ - let typeNameIdOfTupleOrUnit; + const typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); + /** + * Special type name IDs for searching `fn`. + */ + const typeNameIdOfFn = buildTypeMapIndex("fn"); + /** + * Special type name IDs for searching `fnmut`. + */ + const typeNameIdOfFnMut = buildTypeMapIndex("fnmut"); + /** + * Special type name IDs for searching `fnonce`. + */ + const typeNameIdOfFnOnce = buildTypeMapIndex("fnonce"); + /** + * Special type name IDs for searching higher order functions (`->` syntax). + */ + const typeNameIdOfHof = buildTypeMapIndex("->"); /** * Add an item to the type Name->ID map, or, if one already exists, use it. @@ -464,6 +488,21 @@ function initSearch(rawSearchIndex) { } } + function makePrimitiveElement(name, extra) { + return Object.assign({ + name, + id: null, + fullPath: [name], + pathWithoutLast: [], + pathLast: name, + normalizedPathLast: name, + generics: [], + bindings: new Map(), + typeFilter: "primitive", + bindingName: null, + }, extra); + } + /** * @param {ParsedQuery} query * @param {ParserState} parserState @@ -501,18 +540,7 @@ function initSearch(rawSearchIndex) { } const bindingName = parserState.isInBinding; parserState.isInBinding = null; - return { - name: "never", - id: null, - fullPath: ["never"], - pathWithoutLast: [], - pathLast: "never", - normalizedPathLast: "never", - generics: [], - bindings: new Map(), - typeFilter: "primitive", - bindingName, - }; + return makePrimitiveElement("never", { bindingName }); } const quadcolon = /::\s*::/.exec(path); if (path.startsWith("::")) { @@ -558,7 +586,10 @@ function initSearch(rawSearchIndex) { // Syntactically, bindings are parsed as generics, // but the query engine treats them differently. if (gen.bindingName !== null) { - bindings.set(gen.bindingName.name, [gen, ...gen.bindingName.generics]); + if (gen.name !== null) { + gen.bindingName.generics.unshift(gen); + } + bindings.set(gen.bindingName.name, gen.bindingName.generics); return false; } return true; @@ -658,6 +689,38 @@ function initSearch(rawSearchIndex) { return end; } + function getFilteredNextElem(query, parserState, elems, isInGenerics) { + const start = parserState.pos; + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + throw ["Expected type filter before ", ":"]; + } + getNextElem(query, parserState, elems, isInGenerics); + if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { + if (parserState.typeFilter !== null) { + throw [ + "Unexpected ", + ":", + " (expected path after type filter ", + parserState.typeFilter + ":", + ")", + ]; + } + if (elems.length === 0) { + throw ["Expected type filter before ", ":"]; + } else if (query.literalSearch) { + throw ["Cannot use quotes on type filter"]; + } + // The type filter doesn't count as an element since it's a modifier. + const typeFilterElem = elems.pop(); + checkExtraTypeFilterCharacters(start, parserState); + parserState.typeFilter = typeFilterElem.name; + parserState.pos += 1; + parserState.totalElems -= 1; + query.literalSearch = false; + getNextElem(query, parserState, elems, isInGenerics); + } + } + /** * @param {ParsedQuery} query * @param {ParserState} parserState @@ -671,28 +734,19 @@ function initSearch(rawSearchIndex) { let start = parserState.pos; let end; if ("[(".indexOf(parserState.userQuery[parserState.pos]) !== -1) { -let endChar = ")"; -let name = "()"; -let friendlyName = "tuple"; + let endChar = ")"; + let name = "()"; + let friendlyName = "tuple"; -if (parserState.userQuery[parserState.pos] === "[") { - endChar = "]"; - name = "[]"; - friendlyName = "slice"; -} + if (parserState.userQuery[parserState.pos] === "[") { + endChar = "]"; + name = "[]"; + friendlyName = "slice"; + } parserState.pos += 1; const { foundSeparator } = getItemsBefore(query, parserState, generics, endChar); const typeFilter = parserState.typeFilter; - const isInBinding = parserState.isInBinding; - if (typeFilter !== null && typeFilter !== "primitive") { - throw [ - "Invalid search type: primitive ", - name, - " and ", - typeFilter, - " both specified", - ]; - } + const bindingName = parserState.isInBinding; parserState.typeFilter = null; parserState.isInBinding = null; for (const gen of generics) { @@ -702,23 +756,26 @@ if (parserState.userQuery[parserState.pos] === "[") { } if (name === "()" && !foundSeparator && generics.length === 1 && typeFilter === null) { elems.push(generics[0]); + } else if (name === "()" && generics.length === 1 && generics[0].name === "->") { + // `primitive:(a -> b)` parser to `primitive:"->"` + // not `primitive:"()"<"->">` + generics[0].typeFilter = typeFilter; + elems.push(generics[0]); } else { + if (typeFilter !== null && typeFilter !== "primitive") { + throw [ + "Invalid search type: primitive ", + name, + " and ", + typeFilter, + " both specified", + ]; + } parserState.totalElems += 1; if (isInGenerics) { parserState.genericsElems += 1; } - elems.push({ - name: name, - id: null, - fullPath: [name], - pathWithoutLast: [], - pathLast: name, - normalizedPathLast: name, - generics, - bindings: new Map(), - typeFilter: "primitive", - bindingName: isInBinding, - }); + elems.push(makePrimitiveElement(name, { bindingName, generics })); } } else { const isStringElem = parserState.userQuery[start] === "\""; @@ -738,6 +795,32 @@ if (parserState.userQuery[parserState.pos] === "[") { } parserState.pos += 1; getItemsBefore(query, parserState, generics, ">"); + } else if (parserState.pos < parserState.length && + parserState.userQuery[parserState.pos] === "(" + ) { + if (start >= end) { + throw ["Found generics without a path"]; + } + if (parserState.isInBinding) { + throw ["Unexpected ", "(", " after ", "="]; + } + parserState.pos += 1; + const typeFilter = parserState.typeFilter; + parserState.typeFilter = null; + getItemsBefore(query, parserState, generics, ")"); + skipWhitespace(parserState); + if (isReturnArrow(parserState)) { + parserState.pos += 2; + skipWhitespace(parserState); + getFilteredNextElem(query, parserState, generics, isInGenerics); + generics[generics.length - 1].bindingName = makePrimitiveElement("output"); + } else { + generics.push(makePrimitiveElement(null, { + bindingName: makePrimitiveElement("output"), + typeFilter: null, + })); + } + parserState.typeFilter = typeFilter; } if (isStringElem) { skipWhitespace(parserState); @@ -797,7 +880,6 @@ if (parserState.userQuery[parserState.pos] === "[") { function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; let foundSeparator = false; - let start = parserState.pos; // If this is a generic, keep the outer item's type filter around. const oldTypeFilter = parserState.typeFilter; @@ -805,6 +887,19 @@ if (parserState.userQuery[parserState.pos] === "[") { const oldIsInBinding = parserState.isInBinding; parserState.isInBinding = null; + // ML-style Higher Order Function notation + // + // a way to search for any closure or fn pointer regardless of + // which closure trait is used + // + // Looks like this: + // + // `option, (t -> u) -> option` + // ^^^^^^ + // + // The Rust-style closure notation is implemented in getNextElem + let hofParameters = null; + let extra = ""; if (endChar === ">") { extra = "<"; @@ -825,6 +920,21 @@ if (parserState.userQuery[parserState.pos] === "[") { throw ["Unexpected ", endChar, " after ", "="]; } break; + } else if (endChar !== "" && isReturnArrow(parserState)) { + // ML-style HOF notation only works when delimited in something, + // otherwise a function arrow starts the return type of the top + if (parserState.isInBinding) { + throw ["Unexpected ", "->", " after ", "="]; + } + hofParameters = [...elems]; + elems.length = 0; + parserState.pos += 2; + foundStopChar = true; + foundSeparator = false; + continue; + } else if (c === " ") { + parserState.pos += 1; + continue; } else if (isSeparatorCharacter(c)) { parserState.pos += 1; foundStopChar = true; @@ -832,24 +942,6 @@ if (parserState.userQuery[parserState.pos] === "[") { continue; } else if (c === ":" && isPathStart(parserState)) { throw ["Unexpected ", "::", ": paths cannot start with ", "::"]; - } else if (c === ":") { - if (parserState.typeFilter !== null) { - throw ["Unexpected ", ":"]; - } - if (elems.length === 0) { - throw ["Expected type filter before ", ":"]; - } else if (query.literalSearch) { - throw ["Cannot use quotes on type filter"]; - } - // The type filter doesn't count as an element since it's a modifier. - const typeFilterElem = elems.pop(); - checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; - parserState.pos += 1; - parserState.totalElems -= 1; - query.literalSearch = false; - foundStopChar = true; - continue; } else if (isEndCharacter(c)) { throw ["Unexpected ", c, " after ", extra]; } @@ -884,8 +976,7 @@ if (parserState.userQuery[parserState.pos] === "[") { ]; } const posBefore = parserState.pos; - start = parserState.pos; - getNextElem(query, parserState, elems, endChar !== ""); + getFilteredNextElem(query, parserState, elems, endChar !== ""); if (endChar !== "" && parserState.pos >= parserState.length) { throw ["Unclosed ", extra]; } @@ -904,6 +995,27 @@ if (parserState.userQuery[parserState.pos] === "[") { // in any case. parserState.pos += 1; + if (hofParameters) { + // Commas in a HOF don't cause wrapping parens to become a tuple. + // If you want a one-tuple with a HOF in it, write `((a -> b),)`. + foundSeparator = false; + // HOFs can't have directly nested bindings. + if ([...elems, ...hofParameters].some(x => x.bindingName) || parserState.isInBinding) { + throw ["Unexpected ", "=", " within ", "->"]; + } + // HOFs are represented the same way closures are. + // The arguments are wrapped in a tuple, and the output + // is a binding, even though the compiler doesn't technically + // represent fn pointers that way. + const hofElem = makePrimitiveElement("->", { + generics: hofParameters, + bindings: new Map([["output", [...elems]]]), + typeFilter: null, + }); + elems.length = 0; + elems[0] = hofElem; + } + parserState.typeFilter = oldTypeFilter; parserState.isInBinding = oldIsInBinding; @@ -941,7 +1053,6 @@ if (parserState.userQuery[parserState.pos] === "[") { */ function parseInput(query, parserState) { let foundStopChar = true; - let start = parserState.pos; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; @@ -959,29 +1070,6 @@ if (parserState.userQuery[parserState.pos] === "[") { throw ["Unexpected ", c, " after ", parserState.userQuery[parserState.pos - 1]]; } throw ["Unexpected ", c]; - } else if (c === ":" && !isPathStart(parserState)) { - if (parserState.typeFilter !== null) { - throw [ - "Unexpected ", - ":", - " (expected path after type filter ", - parserState.typeFilter + ":", - ")", - ]; - } else if (query.elems.length === 0) { - throw ["Expected type filter before ", ":"]; - } else if (query.literalSearch) { - throw ["Cannot use quotes on type filter"]; - } - // The type filter doesn't count as an element since it's a modifier. - const typeFilterElem = query.elems.pop(); - checkExtraTypeFilterCharacters(start, parserState); - parserState.typeFilter = typeFilterElem.name; - parserState.pos += 1; - parserState.totalElems -= 1; - query.literalSearch = false; - foundStopChar = true; - continue; } else if (c === " ") { skipWhitespace(parserState); continue; @@ -1017,8 +1105,7 @@ if (parserState.userQuery[parserState.pos] === "[") { ]; } const before = query.elems.length; - start = parserState.pos; - getNextElem(query, parserState, query.elems, false); + getFilteredNextElem(query, parserState, query.elems, false); if (query.elems.length === before) { // Nothing was added, weird... Let's increase the position to not remain stuck. parserState.pos += 1; @@ -1258,11 +1345,6 @@ if (parserState.userQuery[parserState.pos] === "[") { * @returns {[ResultObject]} */ function sortResults(results, isType, preferredCrate) { - // if there are no results then return to default and fail - if (results.size === 0) { - return []; - } - const userQuery = parsedQuery.userQuery; const result_list = []; for (const result of results.values()) { @@ -1383,10 +1465,23 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Map|null} mgensIn * - Map functions generics to query generics (never modified). * @param {null|Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {number} unboxingDepth + * - Limit checks that Ty matches Vec, + * but not Vec>>>> * * @return {boolean} - Returns true if a match, false otherwise. */ - function unifyFunctionTypes(fnTypesIn, queryElems, whereClause, mgensIn, solutionCb) { + function unifyFunctionTypes( + fnTypesIn, + queryElems, + whereClause, + mgensIn, + solutionCb, + unboxingDepth + ) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } /** * @type Map|null */ @@ -1405,7 +1500,7 @@ if (parserState.userQuery[parserState.pos] === "[") { && queryElems[0].bindings.size === 0) { const queryElem = queryElems[0]; for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } if (fnType.id < 0 && queryElem.id < 0) { @@ -1424,7 +1519,13 @@ if (parserState.userQuery[parserState.pos] === "[") { } } for (const fnType of fnTypesIn) { - if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + 1 + )) { continue; } if (fnType.id < 0) { @@ -1439,7 +1540,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgensScratch, - solutionCb + solutionCb, + unboxingDepth + 1 )) { return true; } @@ -1448,7 +1550,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgens ? new Map(mgens) : null, - solutionCb + solutionCb, + unboxingDepth + 1 )) { return true; } @@ -1484,7 +1587,7 @@ if (parserState.userQuery[parserState.pos] === "[") { let queryElemsTmp = null; for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } let mgensScratch; @@ -1521,7 +1624,8 @@ if (parserState.userQuery[parserState.pos] === "[") { fnType, queryElem, whereClause, - mgensScratch + mgensScratch, + unboxingDepth ); if (!solution) { return false; @@ -1533,14 +1637,16 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElem.generics, whereClause, simplifiedMgens, - solutionCb + solutionCb, + unboxingDepth ); if (passesUnification) { return true; } } return false; - } + }, + unboxingDepth ); if (passesUnification) { return true; @@ -1552,7 +1658,13 @@ if (parserState.userQuery[parserState.pos] === "[") { } for (let i = flast; i >= 0; i -= 1) { const fnType = fnTypes[i]; - if (!unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens)) { + if (!unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + 1 + )) { continue; } let mgensScratch; @@ -1576,7 +1688,8 @@ if (parserState.userQuery[parserState.pos] === "[") { queryElems, whereClause, mgensScratch, - solutionCb + solutionCb, + unboxingDepth + 1 ); if (passesUnification) { return true; @@ -1595,11 +1708,10 @@ if (parserState.userQuery[parserState.pos] === "[") { * * @param {FunctionType} fnType * @param {QueryElement} queryElem - * @param {[FunctionSearchType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn - Map functions generics to query generics. * @returns {boolean} */ - function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, whereClause, mgensIn) { + function unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgensIn) { // type filters look like `trait:Read` or `enum:Result` if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) { return false; @@ -1635,6 +1747,12 @@ if (parserState.userQuery[parserState.pos] === "[") { ) { // () matches primitive:tuple or primitive:unit // if it matches, then we're fine, and this is an appropriate match candidate + } else if (queryElem.id === typeNameIdOfHof && + (fnType.id === typeNameIdOfFn || fnType.id === typeNameIdOfFnMut || + fnType.id === typeNameIdOfFnOnce) + ) { + // -> matches fn, fnonce, and fnmut + // if it matches, then we're fine, and this is an appropriate match candidate } else if (fnType.id !== queryElem.id || queryElem.id === null) { return false; } @@ -1694,9 +1812,16 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map} mgensIn - Map functions generics to query generics. * Never modified. + * @param {number} unboxingDepth * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} */ - function unifyFunctionTypeCheckBindings(fnType, queryElem, whereClause, mgensIn) { + function unifyFunctionTypeCheckBindings( + fnType, + queryElem, + whereClause, + mgensIn, + unboxingDepth + ) { if (fnType.bindings.size < queryElem.bindings.size) { return false; } @@ -1723,7 +1848,8 @@ if (parserState.userQuery[parserState.pos] === "[") { // return `false` makes unifyFunctionTypes return the full set of // possible solutions return false; - } + }, + unboxingDepth ); return newSolutions; }); @@ -1753,9 +1879,19 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {QueryElement} queryElem * @param {[FunctionType]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth * @returns {boolean} */ - function unifyFunctionTypeIsUnboxCandidate(fnType, queryElem, whereClause, mgens) { + function unifyFunctionTypeIsUnboxCandidate( + fnType, + queryElem, + whereClause, + mgens, + unboxingDepth + ) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } if (fnType.id < 0 && queryElem.id >= 0) { if (!whereClause) { return false; @@ -1777,14 +1913,21 @@ if (parserState.userQuery[parserState.pos] === "[") { whereClause[(-fnType.id) - 1], queryElem, whereClause, - mgensTmp + mgensTmp, + unboxingDepth ); } else if (fnType.generics.length > 0 || fnType.bindings.size > 0) { const simplifiedGenerics = [ ...fnType.generics, ...Array.from(fnType.bindings.values()).flat(), ]; - return checkIfInList(simplifiedGenerics, queryElem, whereClause, mgens); + return checkIfInList( + simplifiedGenerics, + queryElem, + whereClause, + mgens, + unboxingDepth + ); } return false; } @@ -1796,13 +1939,14 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Array} list * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map functions generics to query generics. + * @param {number} unboxingDepth * * @return {boolean} - Returns true if found, false otherwise. */ - function checkIfInList(list, elem, whereClause, mgens) { + function checkIfInList(list, elem, whereClause, mgens, unboxingDepth) { for (const entry of list) { - if (checkType(entry, elem, whereClause, mgens)) { + if (checkType(entry, elem, whereClause, mgens, unboxingDepth)) { return true; } } @@ -1816,29 +1960,40 @@ if (parserState.userQuery[parserState.pos] === "[") { * @param {Row} row * @param {QueryElement} elem - The element from the parsed query. * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map|null} mgens - Map functions generics to query generics. + * @param {Map|null} mgens - Map functions generics to query generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ - function checkType(row, elem, whereClause, mgens) { + function checkType(row, elem, whereClause, mgens, unboxingDepth) { + if (unboxingDepth >= UNBOXING_LIMIT) { + return false; + } if (row.bindings.size === 0 && elem.bindings.size === 0) { - if (elem.id < 0) { - return row.id < 0 || checkIfInList(row.generics, elem, whereClause, mgens); + if (elem.id < 0 && mgens === null) { + return row.id < 0 || checkIfInList( + row.generics, + elem, + whereClause, + mgens, + unboxingDepth + 1 + ); } if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && typePassesFilter(elem.typeFilter, row.ty) && elem.generics.length === 0 && // special case elem.id !== typeNameIdOfArrayOrSlice && elem.id !== typeNameIdOfTupleOrUnit + && elem.id !== typeNameIdOfHof ) { return row.id === elem.id || checkIfInList( row.generics, elem, whereClause, - mgens + mgens, + unboxingDepth ); } } - return unifyFunctionTypes([row], [elem], whereClause, mgens); + return unifyFunctionTypes([row], [elem], whereClause, mgens, null, unboxingDepth); } /** @@ -2053,9 +2208,9 @@ if (parserState.userQuery[parserState.pos] === "[") { ); if (tfpDist !== null) { const in_args = row.type && row.type.inputs - && checkIfInList(row.type.inputs, elem, row.type.where_clause); + && checkIfInList(row.type.inputs, elem, row.type.where_clause, null, 0); const returned = row.type && row.type.output - && checkIfInList(row.type.output, elem, row.type.where_clause); + && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); if (in_args) { results_in_args.max_dist = Math.max(results_in_args.max_dist || 0, tfpDist); const maxDist = results_in_args.size < MAX_RESULTS ? @@ -2141,9 +2296,12 @@ if (parserState.userQuery[parserState.pos] === "[") { row.type.output, parsedQuery.returned, row.type.where_clause, - mgens + mgens, + null, + 0 // unboxing depth ); - } + }, + 0 // unboxing depth )) { return; } @@ -2991,7 +3149,7 @@ ${item.displayPath}${name}\ */ function buildFunctionTypeFingerprint(type, output, fps) { let input = type.id; - // All forms of `[]`/`()` get collapsed down to one thing in the bloom filter. + // All forms of `[]`/`()`/`->` get collapsed down to one thing in the bloom filter. // Differentiating between arrays and slices, if the user asks for it, is // still done in the matching algorithm. if (input === typeNameIdOfArray || input === typeNameIdOfSlice) { @@ -3000,6 +3158,10 @@ ${item.displayPath}${name}\ if (input === typeNameIdOfTuple || input === typeNameIdOfUnit) { input = typeNameIdOfTupleOrUnit; } + if (input === typeNameIdOfFn || input === typeNameIdOfFnMut || + input === typeNameIdOfFnOnce) { + input = typeNameIdOfHof; + } // http://burtleburtle.net/bob/hash/integer.html // ~~ is toInt32. It's used before adding, so // the number stays in safe integer range. @@ -3090,20 +3252,10 @@ ${item.displayPath}${name}\ */ function buildIndex(rawSearchIndex) { searchIndex = []; - typeNameIdMap = new Map(); const charA = "A".charCodeAt(0); let currentIndex = 0; let id = 0; - // Initialize type map indexes for primitive list types - // that can be searched using `[]` syntax. - typeNameIdOfArray = buildTypeMapIndex("array"); - typeNameIdOfSlice = buildTypeMapIndex("slice"); - typeNameIdOfTuple = buildTypeMapIndex("tuple"); - typeNameIdOfUnit = buildTypeMapIndex("unit"); - typeNameIdOfArrayOrSlice = buildTypeMapIndex("[]"); - typeNameIdOfTupleOrUnit = buildTypeMapIndex("()"); - // Function type fingerprints are 128-bit bloom filters that are used to // estimate the distance between function and query. // This loop counts the number of items to allocate a fingerprint for. diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs index 4683965159d7..6e612ea34a70 100644 --- a/src/tools/miri/src/diagnostics.rs +++ b/src/tools/miri/src/diagnostics.rs @@ -528,7 +528,7 @@ pub fn emit_diagnostic(&self, e: NonHaltingDiagnostic) { use NonHaltingDiagnostic::*; let stacktrace = - MiriInterpCx::generate_stacktrace_from_stack(self.threads.active_thread_stack()); + Frame::generate_stacktrace_from_stack(self.threads.active_thread_stack()); let (stacktrace, _was_pruned) = prune_stacktrace(stacktrace, self); let (title, diag_level) = match &e { diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 76f4b77c8f8c..c12fe0e086d8 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -413,7 +413,7 @@ fn call_function( .ok_or_else(|| err_ub_format!("callee has fewer arguments than expected"))?; // Make the local live, and insert the initial value. this.storage_live(local)?; - let callee_arg = this.local_to_place(this.frame_idx(), local)?; + let callee_arg = this.local_to_place(local)?; this.write_immediate(*arg, &callee_arg)?; } if callee_args.next().is_some() { diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 4dbb814fc27d..7e5518392d8a 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1488,14 +1488,13 @@ fn after_stack_pop( fn after_local_allocated( ecx: &mut InterpCx<'mir, 'tcx, Self>, - frame: usize, local: mir::Local, mplace: &MPlaceTy<'tcx, Provenance>, ) -> InterpResult<'tcx> { let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else { panic!("after_local_allocated should only be called on fresh allocations"); }; - let local_decl = &ecx.active_thread_stack()[frame].body.local_decls[local]; + let local_decl = &ecx.frame().body.local_decls[local]; let span = local_decl.source_info.span; ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None)); Ok(()) diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation.stdout b/src/tools/miri/tests/pass/shims/time-with-isolation.stdout index f3d071e001e5..ff5889bacd5d 100644 --- a/src/tools/miri/tests/pass/shims/time-with-isolation.stdout +++ b/src/tools/miri/tests/pass/shims/time-with-isolation.stdout @@ -1,2 +1,2 @@ -The loop took around 7s +The loop took around 12s (It's fine for this number to change when you `--bless` this test.) diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 91bbf5041ff5..0ef962c2df87 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -1107,7 +1107,6 @@ "ui/generic-associated-types/issue-92954.rs", "ui/generic-associated-types/issue-93141.rs", "ui/generic-associated-types/issue-93262.rs", -"ui/generic-associated-types/issue-93340.rs", "ui/generic-associated-types/issue-93341.rs", "ui/generic-associated-types/issue-93342.rs", "ui/generic-associated-types/issue-93874.rs", diff --git a/tests/codegen/align-byval-alignment-mismatch.rs b/tests/codegen/align-byval-alignment-mismatch.rs new file mode 100644 index 000000000000..306e3ce1358a --- /dev/null +++ b/tests/codegen/align-byval-alignment-mismatch.rs @@ -0,0 +1,126 @@ +// ignore-tidy-linelength +//@ revisions:i686-linux x86_64-linux + +//@[i686-linux] compile-flags: --target i686-unknown-linux-gnu +//@[i686-linux] needs-llvm-components: x86 +//@[x86_64-linux] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64-linux] needs-llvm-components: x86 + +// Tests that we correctly copy arguments into allocas when the alignment of the byval argument +// is different from the alignment of the Rust type. + +// For the following test cases: +// All of the `*_decreases_alignment` functions should codegen to a direct call, since the +// alignment is already sufficient. +// All off the `*_increases_alignment` functions should copy the argument to an alloca +// on i686-unknown-linux-gnu, since the alignment needs to be increased, and should codegen +// to a direct call on x86_64-unknown-linux-gnu, where byval alignment matches Rust alignment. + +#![feature(no_core, lang_items)] +#![crate_type = "lib"] +#![no_std] +#![no_core] +#![allow(non_camel_case_types)] + +#[lang = "sized"] +trait Sized {} +#[lang = "freeze"] +trait Freeze {} +#[lang = "copy"] +trait Copy {} + +// This type has align 1 in Rust, but as a byval argument on i686-linux, it will have align 4. +#[repr(C)] +#[repr(packed)] +struct Align1 { + x: u128, + y: u128, + z: u128, +} + +// This type has align 16 in Rust, but as a byval argument on i686-linux, it will have align 4. +#[repr(C)] +#[repr(align(16))] +struct Align16 { + x: u128, + y: u128, + z: u128, +} + +extern "C" { + fn extern_c_align1(x: Align1); + fn extern_c_align16(x: Align16); +} + +// CHECK-LABEL: @rust_to_c_increases_alignment +#[no_mangle] +pub unsafe fn rust_to_c_increases_alignment(x: Align1) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align1, align 4 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x + // i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_c_align1 + extern_c_align1(x); +} + +// CHECK-LABEL: @rust_to_c_decreases_alignment +#[no_mangle] +pub unsafe fn rust_to_c_decreases_alignment(x: Align16) { + // CHECK: start: + // CHECK-NEXT: call void @extern_c_align16 + extern_c_align16(x); +} + +extern "Rust" { + fn extern_rust_align1(x: Align1); + fn extern_rust_align16(x: Align16); +} + +// CHECK-LABEL: @c_to_rust_decreases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_decreases_alignment(x: Align1) { + // CHECK: start: + // CHECK-NEXT: call void @extern_rust_align1 + extern_rust_align1(x); +} + +// CHECK-LABEL: @c_to_rust_increases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_increases_alignment(x: Align16) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0 + // i686-linux-NEXT: call void @extern_rust_align16({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_rust_align16 + extern_rust_align16(x); +} + +extern "Rust" { + fn extern_rust_ref_align1(x: &Align1); + fn extern_rust_ref_align16(x: &Align16); +} + +// CHECK-LABEL: @c_to_rust_ref_decreases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_ref_decreases_alignment(x: Align1) { + // CHECK: start: + // CHECK-NEXT: call void @extern_rust_ref_align1 + extern_rust_ref_align1(&x); +} + +// CHECK-LABEL: @c_to_rust_ref_increases_alignment +#[no_mangle] +pub unsafe extern "C" fn c_to_rust_ref_increases_alignment(x: Align16) { + // i686-linux: start: + // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca %Align16, align 16 + // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 16 {{.*}}[[ALLOCA]], ptr {{.*}}align 4 {{.*}}%0 + // i686-linux-NEXT: call void @extern_rust_ref_align16({{.+}} [[ALLOCA]]) + + // x86_64-linux: start: + // x86_64-linux-NEXT: call void @extern_rust_ref_align16 + extern_rust_ref_align16(&x); +} diff --git a/tests/coverage/branch_generics.cov-map b/tests/coverage/branch_generics.cov-map new file mode 100644 index 000000000000..719e97efad45 --- /dev/null +++ b/tests/coverage/branch_generics.cov-map @@ -0,0 +1,54 @@ +Function name: branch_generics::print_size::<()> +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: branch_generics::print_size:: +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + +Function name: branch_generics::print_size:: +Raw bytes (35): 0x[01, 01, 02, 01, 05, 05, 02, 05, 01, 06, 01, 01, 24, 20, 05, 02, 01, 08, 00, 24, 05, 00, 25, 02, 06, 02, 02, 0c, 02, 06, 07, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 2 +- expression 0 operands: lhs = Counter(0), rhs = Counter(1) +- expression 1 operands: lhs = Counter(1), rhs = Expression(0, Sub) +Number of file 0 mappings: 5 +- Code(Counter(0)) at (prev + 6, 1) to (start + 1, 36) +- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 1, 8) to (start + 0, 36) + true = c1 + false = (c0 - c1) +- Code(Counter(1)) at (prev + 0, 37) to (start + 2, 6) +- Code(Expression(0, Sub)) at (prev + 2, 12) to (start + 2, 6) + = (c0 - c1) +- Code(Expression(1, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c1 + (c0 - c1)) + diff --git a/tests/coverage/branch_generics.coverage b/tests/coverage/branch_generics.coverage new file mode 100644 index 000000000000..e7cec151ce62 --- /dev/null +++ b/tests/coverage/branch_generics.coverage @@ -0,0 +1,62 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| 3|fn print_size() { + LL| 3| if std::mem::size_of::() > 4 { + ------------------ + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 0, False: 1] + | Branch (LL:8): [True: 1, False: 0] + ------------------ + LL| 1| println!("size > 4"); + LL| 2| } else { + LL| 2| println!("size <= 4"); + LL| 2| } + LL| 3|} + ------------------ + | branch_generics::print_size::<()>: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ + | branch_generics::print_size::: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 0, False: 1] + | ------------------ + | LL| 0| println!("size > 4"); + | LL| 1| } else { + | LL| 1| println!("size <= 4"); + | LL| 1| } + | LL| 1|} + ------------------ + | branch_generics::print_size::: + | LL| 1|fn print_size() { + | LL| 1| if std::mem::size_of::() > 4 { + | ------------------ + | | Branch (LL:8): [True: 1, False: 0] + | ------------------ + | LL| 1| println!("size > 4"); + | LL| 1| } else { + | LL| 0| println!("size <= 4"); + | LL| 0| } + | LL| 1|} + ------------------ + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | print_size::<()>(); + LL| | print_size::(); + LL| | print_size::(); + LL| |} + diff --git a/tests/coverage/branch_generics.rs b/tests/coverage/branch_generics.rs new file mode 100644 index 000000000000..d870ace7006b --- /dev/null +++ b/tests/coverage/branch_generics.rs @@ -0,0 +1,19 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +fn print_size() { + if std::mem::size_of::() > 4 { + println!("size > 4"); + } else { + println!("size <= 4"); + } +} + +#[coverage(off)] +fn main() { + print_size::<()>(); + print_size::(); + print_size::(); +} diff --git a/tests/coverage/branch_guard.cov-map b/tests/coverage/branch_guard.cov-map new file mode 100644 index 000000000000..0b3622f6347a --- /dev/null +++ b/tests/coverage/branch_guard.cov-map @@ -0,0 +1,32 @@ +Function name: branch_guard::branch_match_guard +Raw bytes (85): 0x[01, 01, 06, 19, 0d, 05, 09, 0f, 15, 13, 11, 17, 0d, 05, 09, 0d, 01, 0c, 01, 01, 10, 1d, 03, 0b, 00, 0c, 15, 01, 14, 02, 0a, 0d, 03, 0e, 00, 0f, 19, 00, 14, 00, 19, 20, 0d, 02, 00, 14, 00, 1e, 0d, 00, 1d, 02, 0a, 11, 03, 0e, 00, 0f, 1d, 00, 14, 00, 19, 20, 11, 09, 00, 14, 00, 1e, 11, 00, 1d, 02, 0a, 17, 03, 0e, 02, 0a, 0b, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 6 +- expression 0 operands: lhs = Counter(6), rhs = Counter(3) +- expression 1 operands: lhs = Counter(1), rhs = Counter(2) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(5) +- expression 3 operands: lhs = Expression(4, Add), rhs = Counter(4) +- expression 4 operands: lhs = Expression(5, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(1), rhs = Counter(2) +Number of file 0 mappings: 13 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(7)) at (prev + 3, 11) to (start + 0, 12) +- Code(Counter(5)) at (prev + 1, 20) to (start + 2, 10) +- Code(Counter(3)) at (prev + 3, 14) to (start + 0, 15) +- Code(Counter(6)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 30) + true = c3 + false = (c6 - c3) +- Code(Counter(3)) at (prev + 0, 29) to (start + 2, 10) +- Code(Counter(4)) at (prev + 3, 14) to (start + 0, 15) +- Code(Counter(7)) at (prev + 0, 20) to (start + 0, 25) +- Branch { true: Counter(4), false: Counter(2) } at (prev + 0, 20) to (start + 0, 30) + true = c4 + false = c2 +- Code(Counter(4)) at (prev + 0, 29) to (start + 2, 10) +- Code(Expression(5, Add)) at (prev + 3, 14) to (start + 2, 10) + = (c1 + c2) +- Code(Expression(2, Add)) at (prev + 4, 1) to (start + 0, 2) + = ((((c1 + c2) + c3) + c4) + c5) + diff --git a/tests/coverage/branch_guard.coverage b/tests/coverage/branch_guard.coverage new file mode 100644 index 000000000000..f89b965b5d0f --- /dev/null +++ b/tests/coverage/branch_guard.coverage @@ -0,0 +1,45 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 4|fn branch_match_guard(x: Option) { + LL| 4| no_merge!(); + LL| | + LL| 1| match x { + LL| 1| Some(0) => { + LL| 1| println!("zero"); + LL| 1| } + LL| 3| Some(x) if x % 2 == 0 => { + ^2 + ------------------ + | Branch (LL:20): [True: 2, False: 1] + ------------------ + LL| 2| println!("is nonzero and even"); + LL| 2| } + LL| 1| Some(x) if x % 3 == 0 => { + ------------------ + | Branch (LL:20): [True: 1, False: 0] + ------------------ + LL| 1| println!("is nonzero and odd, but divisible by 3"); + LL| 1| } + LL| 0| _ => { + LL| 0| println!("something else"); + LL| 0| } + LL| | } + LL| 4|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | branch_match_guard(Some(0)); + LL| | branch_match_guard(Some(2)); + LL| | branch_match_guard(Some(6)); + LL| | branch_match_guard(Some(3)); + LL| |} + diff --git a/tests/coverage/branch_guard.rs b/tests/coverage/branch_guard.rs new file mode 100644 index 000000000000..fa049e6206dc --- /dev/null +++ b/tests/coverage/branch_guard.rs @@ -0,0 +1,37 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn branch_match_guard(x: Option) { + no_merge!(); + + match x { + Some(0) => { + println!("zero"); + } + Some(x) if x % 2 == 0 => { + println!("is nonzero and even"); + } + Some(x) if x % 3 == 0 => { + println!("is nonzero and odd, but divisible by 3"); + } + _ => { + println!("something else"); + } + } +} + +#[coverage(off)] +fn main() { + branch_match_guard(Some(0)); + branch_match_guard(Some(2)); + branch_match_guard(Some(6)); + branch_match_guard(Some(3)); +} diff --git a/tests/coverage/branch_if.cov-map b/tests/coverage/branch_if.cov-map new file mode 100644 index 000000000000..0dbfd92541b0 --- /dev/null +++ b/tests/coverage/branch_if.cov-map @@ -0,0 +1,188 @@ +Function name: branch_if::branch_and +Raw bytes (56): 0x[01, 01, 04, 05, 09, 0d, 02, 11, 0f, 0d, 02, 08, 01, 2b, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 00, 0d, 00, 0e, 20, 11, 0d, 00, 0d, 00, 0e, 11, 00, 0f, 02, 06, 0f, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(3), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Counter(4), rhs = Expression(3, Add) +- expression 3 operands: lhs = Counter(3), rhs = Expression(0, Sub) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 43, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) +- Code(Counter(2)) at (prev + 0, 13) to (start + 0, 14) +- Branch { true: Counter(4), false: Counter(3) } at (prev + 0, 13) to (start + 0, 14) + true = c4 + false = c3 +- Code(Counter(4)) at (prev + 0, 15) to (start + 2, 6) +- Code(Expression(3, Add)) at (prev + 2, 12) to (start + 2, 6) + = (c3 + (c1 - c2)) +- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) + = (c4 + (c3 + (c1 - c2))) + +Function name: branch_if::branch_not +Raw bytes (224): 0x[01, 01, 29, 05, 09, 09, 02, a3, 01, 0d, 09, 02, a3, 01, 0d, 09, 02, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 15, 8e, 01, 93, 01, 15, 11, 96, 01, 9b, 01, 11, 0d, 9e, 01, a3, 01, 0d, 09, 02, 12, 01, 0c, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 09, 01, 09, 00, 11, 02, 01, 06, 00, 07, a3, 01, 01, 08, 00, 0a, 20, 9e, 01, 0d, 00, 08, 00, 0a, 9e, 01, 00, 0b, 02, 06, 0d, 02, 06, 00, 07, 9b, 01, 01, 08, 00, 0b, 20, 11, 96, 01, 00, 08, 00, 0b, 11, 00, 0c, 02, 06, 96, 01, 02, 06, 00, 07, 93, 01, 01, 08, 00, 0c, 20, 8e, 01, 15, 00, 08, 00, 0c, 8e, 01, 00, 0d, 02, 06, 15, 02, 06, 00, 07, 8b, 01, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 41 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 6 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 7 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 9 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 10 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 11 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 13 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 15 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 17 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 18 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 20 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 22 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 23 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 24 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 25 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 26 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 27 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 28 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 29 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 30 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 31 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 33 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 34 operands: lhs = Counter(5), rhs = Expression(35, Sub) +- expression 35 operands: lhs = Expression(36, Add), rhs = Counter(5) +- expression 36 operands: lhs = Counter(4), rhs = Expression(37, Sub) +- expression 37 operands: lhs = Expression(38, Add), rhs = Counter(4) +- expression 38 operands: lhs = Counter(3), rhs = Expression(39, Sub) +- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(3) +- expression 40 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 18 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) +- Code(Counter(2)) at (prev + 1, 9) to (start + 0, 17) +- Code(Expression(0, Sub)) at (prev + 1, 6) to (start + 0, 7) + = (c1 - c2) +- Code(Expression(40, Add)) at (prev + 1, 8) to (start + 0, 10) + = (c2 + (c1 - c2)) +- Branch { true: Expression(39, Sub), false: Counter(3) } at (prev + 0, 8) to (start + 0, 10) + true = ((c2 + (c1 - c2)) - c3) + false = c3 +- Code(Expression(39, Sub)) at (prev + 0, 11) to (start + 2, 6) + = ((c2 + (c1 - c2)) - c3) +- Code(Counter(3)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(38, Add)) at (prev + 1, 8) to (start + 0, 11) + = (c3 + ((c2 + (c1 - c2)) - c3)) +- Branch { true: Counter(4), false: Expression(37, Sub) } at (prev + 0, 8) to (start + 0, 11) + true = c4 + false = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Counter(4)) at (prev + 0, 12) to (start + 2, 6) +- Code(Expression(37, Sub)) at (prev + 2, 6) to (start + 0, 7) + = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Expression(36, Add)) at (prev + 1, 8) to (start + 0, 12) + = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) +- Branch { true: Expression(35, Sub), false: Counter(5) } at (prev + 0, 8) to (start + 0, 12) + true = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) + false = c5 +- Code(Expression(35, Sub)) at (prev + 0, 13) to (start + 2, 6) + = ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5) +- Code(Counter(5)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(34, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c5 + ((c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) - c5)) + +Function name: branch_if::branch_not_as +Raw bytes (124): 0x[01, 01, 16, 05, 09, 09, 02, 57, 0d, 09, 02, 57, 0d, 09, 02, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 4f, 11, 0d, 52, 57, 0d, 09, 02, 11, 4a, 4f, 11, 0d, 52, 57, 0d, 09, 02, 0e, 01, 1d, 01, 01, 10, 05, 03, 08, 00, 14, 20, 02, 09, 00, 08, 00, 14, 02, 00, 15, 02, 06, 09, 02, 06, 00, 07, 57, 01, 08, 00, 15, 20, 0d, 52, 00, 08, 00, 15, 0d, 00, 16, 02, 06, 52, 02, 06, 00, 07, 4f, 01, 08, 00, 16, 20, 4a, 11, 00, 08, 00, 16, 4a, 00, 17, 02, 06, 11, 02, 06, 00, 07, 47, 01, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 22 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 2 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 4 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 5 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 6 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 7 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 8 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 9 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 10 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 11 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 12 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 13 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 14 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 15 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 16 operands: lhs = Counter(2), rhs = Expression(0, Sub) +- expression 17 operands: lhs = Counter(4), rhs = Expression(18, Sub) +- expression 18 operands: lhs = Expression(19, Add), rhs = Counter(4) +- expression 19 operands: lhs = Counter(3), rhs = Expression(20, Sub) +- expression 20 operands: lhs = Expression(21, Add), rhs = Counter(3) +- expression 21 operands: lhs = Counter(2), rhs = Expression(0, Sub) +Number of file 0 mappings: 14 +- Code(Counter(0)) at (prev + 29, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 20) +- Branch { true: Expression(0, Sub), false: Counter(2) } at (prev + 0, 8) to (start + 0, 20) + true = (c1 - c2) + false = c2 +- Code(Expression(0, Sub)) at (prev + 0, 21) to (start + 2, 6) + = (c1 - c2) +- Code(Counter(2)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(21, Add)) at (prev + 1, 8) to (start + 0, 21) + = (c2 + (c1 - c2)) +- Branch { true: Counter(3), false: Expression(20, Sub) } at (prev + 0, 8) to (start + 0, 21) + true = c3 + false = ((c2 + (c1 - c2)) - c3) +- Code(Counter(3)) at (prev + 0, 22) to (start + 2, 6) +- Code(Expression(20, Sub)) at (prev + 2, 6) to (start + 0, 7) + = ((c2 + (c1 - c2)) - c3) +- Code(Expression(19, Add)) at (prev + 1, 8) to (start + 0, 22) + = (c3 + ((c2 + (c1 - c2)) - c3)) +- Branch { true: Expression(18, Sub), false: Counter(4) } at (prev + 0, 8) to (start + 0, 22) + true = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) + false = c4 +- Code(Expression(18, Sub)) at (prev + 0, 23) to (start + 2, 6) + = ((c3 + ((c2 + (c1 - c2)) - c3)) - c4) +- Code(Counter(4)) at (prev + 2, 6) to (start + 0, 7) +- Code(Expression(17, Add)) at (prev + 1, 1) to (start + 0, 2) + = (c4 + ((c3 + ((c2 + (c1 - c2)) - c3)) - c4)) + +Function name: branch_if::branch_or +Raw bytes (56): 0x[01, 01, 04, 05, 09, 09, 0d, 0f, 11, 09, 0d, 08, 01, 35, 01, 01, 10, 05, 03, 08, 00, 09, 20, 09, 02, 00, 08, 00, 09, 02, 00, 0d, 00, 0e, 20, 0d, 11, 00, 0d, 00, 0e, 0f, 00, 0f, 02, 06, 11, 02, 0c, 02, 06, 0b, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(3, Add), rhs = Counter(4) +- expression 3 operands: lhs = Counter(2), rhs = Counter(3) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 53, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 8) to (start + 0, 9) +- Branch { true: Counter(2), false: Expression(0, Sub) } at (prev + 0, 8) to (start + 0, 9) + true = c2 + false = (c1 - c2) +- Code(Expression(0, Sub)) at (prev + 0, 13) to (start + 0, 14) + = (c1 - c2) +- Branch { true: Counter(3), false: Counter(4) } at (prev + 0, 13) to (start + 0, 14) + true = c3 + false = c4 +- Code(Expression(3, Add)) at (prev + 0, 15) to (start + 2, 6) + = (c2 + c3) +- Code(Counter(4)) at (prev + 2, 12) to (start + 2, 6) +- Code(Expression(2, Add)) at (prev + 3, 1) to (start + 0, 2) + = ((c2 + c3) + c4) + diff --git a/tests/coverage/branch_if.coverage b/tests/coverage/branch_if.coverage new file mode 100644 index 000000000000..2a9a408b16af --- /dev/null +++ b/tests/coverage/branch_if.coverage @@ -0,0 +1,115 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 3|fn branch_not(a: bool) { + LL| 3| no_merge!(); + LL| | + LL| 3| if a { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ + LL| 2| say("a") + LL| 1| } + LL| 3| if !a { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ + LL| 1| say("not a"); + LL| 2| } + LL| 3| if !!a { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ + LL| 2| say("not not a"); + LL| 2| } + ^1 + LL| 3| if !!!a { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ + LL| 1| say("not not not a"); + LL| 2| } + LL| 3|} + LL| | + LL| 3|fn branch_not_as(a: bool) { + LL| 3| no_merge!(); + LL| | + LL| 3| if !(a as bool) { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ + LL| 1| say("not (a as bool)"); + LL| 2| } + LL| 3| if !!(a as bool) { + ------------------ + | Branch (LL:8): [True: 2, False: 1] + ------------------ + LL| 2| say("not not (a as bool)"); + LL| 2| } + ^1 + LL| 3| if !!!(a as bool) { + ------------------ + | Branch (LL:8): [True: 1, False: 2] + ------------------ + LL| 1| say("not not (a as bool)"); + LL| 2| } + LL| 3|} + LL| | + LL| 15|fn branch_and(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| 15| if a && b { + ^12 + ------------------ + | Branch (LL:8): [True: 12, False: 3] + | Branch (LL:13): [True: 8, False: 4] + ------------------ + LL| 8| say("both"); + LL| 8| } else { + LL| 7| say("not both"); + LL| 7| } + LL| 15|} + LL| | + LL| 15|fn branch_or(a: bool, b: bool) { + LL| 15| no_merge!(); + LL| | + LL| 15| if a || b { + ^3 + ------------------ + | Branch (LL:8): [True: 12, False: 3] + | Branch (LL:13): [True: 2, False: 1] + ------------------ + LL| 14| say("either"); + LL| 14| } else { + LL| 1| say("neither"); + LL| 1| } + LL| 15|} + LL| | + LL| |#[coverage(off)] + LL| |fn say(message: &str) { + LL| | core::hint::black_box(message); + LL| |} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | for a in [false, true, true] { + LL| | branch_not(a); + LL| | branch_not_as(a); + LL| | } + LL| | + LL| | for a in [false, true, true, true, true] { + LL| | for b in [false, true, true] { + LL| | branch_and(a, b); + LL| | branch_or(a, b); + LL| | } + LL| | } + LL| |} + diff --git a/tests/coverage/branch_if.rs b/tests/coverage/branch_if.rs new file mode 100644 index 000000000000..151eede75bbc --- /dev/null +++ b/tests/coverage/branch_if.rs @@ -0,0 +1,81 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn branch_not(a: bool) { + no_merge!(); + + if a { + say("a") + } + if !a { + say("not a"); + } + if !!a { + say("not not a"); + } + if !!!a { + say("not not not a"); + } +} + +fn branch_not_as(a: bool) { + no_merge!(); + + if !(a as bool) { + say("not (a as bool)"); + } + if !!(a as bool) { + say("not not (a as bool)"); + } + if !!!(a as bool) { + say("not not (a as bool)"); + } +} + +fn branch_and(a: bool, b: bool) { + no_merge!(); + + if a && b { + say("both"); + } else { + say("not both"); + } +} + +fn branch_or(a: bool, b: bool) { + no_merge!(); + + if a || b { + say("either"); + } else { + say("neither"); + } +} + +#[coverage(off)] +fn say(message: &str) { + core::hint::black_box(message); +} + +#[coverage(off)] +fn main() { + for a in [false, true, true] { + branch_not(a); + branch_not_as(a); + } + + for a in [false, true, true, true, true] { + for b in [false, true, true] { + branch_and(a, b); + branch_or(a, b); + } + } +} diff --git a/tests/coverage/branch_while.cov-map b/tests/coverage/branch_while.cov-map new file mode 100644 index 000000000000..d5f54f1abea2 --- /dev/null +++ b/tests/coverage/branch_while.cov-map @@ -0,0 +1,98 @@ +Function name: branch_while::while_cond +Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 0c, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 10, 20, 09, 0a, 00, 0b, 00, 10, 09, 00, 11, 02, 06, 0a, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 12, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) +- Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 16) + = (c1 + c2) +- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 16) + true = c2 + false = ((c1 + c2) - c2) +- Code(Counter(2)) at (prev + 0, 17) to (start + 2, 6) +- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) + = ((c1 + c2) - c2) + +Function name: branch_while::while_cond_not +Raw bytes (42): 0x[01, 01, 03, 05, 09, 03, 09, 03, 09, 06, 01, 15, 01, 01, 10, 05, 03, 09, 00, 12, 03, 01, 0b, 00, 14, 20, 09, 0a, 00, 0b, 00, 14, 09, 00, 15, 02, 06, 0a, 03, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 3 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 6 +- Code(Counter(0)) at (prev + 21, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 0, 18) +- Code(Expression(0, Add)) at (prev + 1, 11) to (start + 0, 20) + = (c1 + c2) +- Branch { true: Counter(2), false: Expression(2, Sub) } at (prev + 0, 11) to (start + 0, 20) + true = c2 + false = ((c1 + c2) - c2) +- Code(Counter(2)) at (prev + 0, 21) to (start + 2, 6) +- Code(Expression(2, Sub)) at (prev + 3, 1) to (start + 0, 2) + = ((c1 + c2) - c2) + +Function name: branch_while::while_op_and +Raw bytes (56): 0x[01, 01, 04, 05, 09, 03, 0d, 03, 0d, 11, 0d, 08, 01, 1e, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 0a, 0d, 00, 0b, 00, 10, 0a, 00, 14, 00, 19, 20, 09, 11, 00, 14, 00, 19, 09, 00, 1a, 03, 06, 0f, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 4 +- expression 0 operands: lhs = Counter(1), rhs = Counter(2) +- expression 1 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(3) +- expression 3 operands: lhs = Counter(4), rhs = Counter(3) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 30, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) + = (c1 + c2) +- Branch { true: Expression(2, Sub), false: Counter(3) } at (prev + 0, 11) to (start + 0, 16) + true = ((c1 + c2) - c3) + false = c3 +- Code(Expression(2, Sub)) at (prev + 0, 20) to (start + 0, 25) + = ((c1 + c2) - c3) +- Branch { true: Counter(2), false: Counter(4) } at (prev + 0, 20) to (start + 0, 25) + true = c2 + false = c4 +- Code(Counter(2)) at (prev + 0, 26) to (start + 3, 6) +- Code(Expression(3, Add)) at (prev + 4, 1) to (start + 0, 2) + = (c4 + c3) + +Function name: branch_while::while_op_or +Raw bytes (66): 0x[01, 01, 09, 05, 1b, 09, 0d, 03, 09, 03, 09, 22, 0d, 03, 09, 09, 0d, 22, 0d, 03, 09, 08, 01, 29, 01, 01, 10, 05, 03, 09, 01, 12, 03, 02, 0b, 00, 10, 20, 09, 22, 00, 0b, 00, 10, 22, 00, 14, 00, 19, 20, 0d, 1e, 00, 14, 00, 19, 1b, 00, 1a, 03, 06, 1e, 04, 01, 00, 02] +Number of files: 1 +- file 0 => global file 1 +Number of expressions: 9 +- expression 0 operands: lhs = Counter(1), rhs = Expression(6, Add) +- expression 1 operands: lhs = Counter(2), rhs = Counter(3) +- expression 2 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 3 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 4 operands: lhs = Expression(8, Sub), rhs = Counter(3) +- expression 5 operands: lhs = Expression(0, Add), rhs = Counter(2) +- expression 6 operands: lhs = Counter(2), rhs = Counter(3) +- expression 7 operands: lhs = Expression(8, Sub), rhs = Counter(3) +- expression 8 operands: lhs = Expression(0, Add), rhs = Counter(2) +Number of file 0 mappings: 8 +- Code(Counter(0)) at (prev + 41, 1) to (start + 1, 16) +- Code(Counter(1)) at (prev + 3, 9) to (start + 1, 18) +- Code(Expression(0, Add)) at (prev + 2, 11) to (start + 0, 16) + = (c1 + (c2 + c3)) +- Branch { true: Counter(2), false: Expression(8, Sub) } at (prev + 0, 11) to (start + 0, 16) + true = c2 + false = ((c1 + (c2 + c3)) - c2) +- Code(Expression(8, Sub)) at (prev + 0, 20) to (start + 0, 25) + = ((c1 + (c2 + c3)) - c2) +- Branch { true: Counter(3), false: Expression(7, Sub) } at (prev + 0, 20) to (start + 0, 25) + true = c3 + false = (((c1 + (c2 + c3)) - c2) - c3) +- Code(Expression(6, Add)) at (prev + 0, 26) to (start + 3, 6) + = (c2 + c3) +- Code(Expression(7, Sub)) at (prev + 4, 1) to (start + 0, 2) + = (((c1 + (c2 + c3)) - c2) - c3) + diff --git a/tests/coverage/branch_while.coverage b/tests/coverage/branch_while.coverage new file mode 100644 index 000000000000..8d9a6c3bc68b --- /dev/null +++ b/tests/coverage/branch_while.coverage @@ -0,0 +1,74 @@ + LL| |#![feature(coverage_attribute)] + LL| |//@ edition: 2021 + LL| |//@ compile-flags: -Zcoverage-options=branch + LL| |//@ llvm-cov-flags: --show-branches=count + LL| | + LL| |macro_rules! no_merge { + LL| | () => { + LL| | for _ in 0..1 {} + LL| | }; + LL| |} + LL| | + LL| 1|fn while_cond() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 9| while a > 0 { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ + LL| 8| a -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| 1|fn while_cond_not() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 9| while !(a == 0) { + ------------------ + | Branch (LL:11): [True: 8, False: 1] + ------------------ + LL| 8| a -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| 1|fn while_op_and() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 8; + LL| 1| let mut b = 4; + LL| 5| while a > 0 && b > 0 { + ------------------ + | Branch (LL:11): [True: 5, False: 0] + | Branch (LL:20): [True: 4, False: 1] + ------------------ + LL| 4| a -= 1; + LL| 4| b -= 1; + LL| 4| } + LL| 1|} + LL| | + LL| 1|fn while_op_or() { + LL| 1| no_merge!(); + LL| | + LL| 1| let mut a = 4; + LL| 1| let mut b = 8; + LL| 9| while a > 0 || b > 0 { + ^5 + ------------------ + | Branch (LL:11): [True: 4, False: 5] + | Branch (LL:20): [True: 4, False: 1] + ------------------ + LL| 8| a -= 1; + LL| 8| b -= 1; + LL| 8| } + LL| 1|} + LL| | + LL| |#[coverage(off)] + LL| |fn main() { + LL| | while_cond(); + LL| | while_cond_not(); + LL| | while_op_and(); + LL| | while_op_or(); + LL| |} + diff --git a/tests/coverage/branch_while.rs b/tests/coverage/branch_while.rs new file mode 100644 index 000000000000..507815fbecbe --- /dev/null +++ b/tests/coverage/branch_while.rs @@ -0,0 +1,58 @@ +#![feature(coverage_attribute)] +//@ edition: 2021 +//@ compile-flags: -Zcoverage-options=branch +//@ llvm-cov-flags: --show-branches=count + +macro_rules! no_merge { + () => { + for _ in 0..1 {} + }; +} + +fn while_cond() { + no_merge!(); + + let mut a = 8; + while a > 0 { + a -= 1; + } +} + +fn while_cond_not() { + no_merge!(); + + let mut a = 8; + while !(a == 0) { + a -= 1; + } +} + +fn while_op_and() { + no_merge!(); + + let mut a = 8; + let mut b = 4; + while a > 0 && b > 0 { + a -= 1; + b -= 1; + } +} + +fn while_op_or() { + no_merge!(); + + let mut a = 4; + let mut b = 8; + while a > 0 || b > 0 { + a -= 1; + b -= 1; + } +} + +#[coverage(off)] +fn main() { + while_cond(); + while_cond_not(); + while_op_and(); + while_op_or(); +} diff --git a/tests/rustdoc-js-std/parser-errors.js b/tests/rustdoc-js-std/parser-errors.js index 16d171260dad..ffd169812b63 100644 --- a/tests/rustdoc-js-std/parser-errors.js +++ b/tests/rustdoc-js-std/parser-errors.js @@ -114,7 +114,7 @@ const PARSED = [ original: "(p -> p", returned: [], userQuery: "(p -> p", - error: "Unexpected `-` after `(`", + error: "Unclosed `(`", }, { query: "::a::b", @@ -195,7 +195,7 @@ const PARSED = [ original: "a (b:", returned: [], userQuery: "a (b:", - error: "Expected `,`, `:` or `->`, found `(`", + error: "Unclosed `(`", }, { query: "_:", @@ -330,7 +330,7 @@ const PARSED = [ original: 'a<->', returned: [], userQuery: 'a<->', - error: 'Unexpected `-` after `<`', + error: 'Unclosed `<`', }, { query: "a:", @@ -357,7 +357,16 @@ const PARSED = [ original: "a,:", returned: [], userQuery: "a,:", - error: 'Unexpected `,` in type filter (before `:`)', + error: 'Expected type filter before `:`', + }, + { + query: "a!:", + elems: [], + foundElems: 0, + original: "a!:", + returned: [], + userQuery: "a!:", + error: 'Unexpected `!` in type filter (before `:`)', }, { query: " a<> :", @@ -366,7 +375,7 @@ const PARSED = [ original: "a<> :", returned: [], userQuery: "a<> :", - error: 'Unexpected `<` in type filter (before `:`)', + error: 'Expected `,`, `:` or `->` after `>`, found `:`', }, { query: "mod : :", diff --git a/tests/rustdoc-js-std/parser-hof.js b/tests/rustdoc-js-std/parser-hof.js new file mode 100644 index 000000000000..0b99c45b7a92 --- /dev/null +++ b/tests/rustdoc-js-std/parser-hof.js @@ -0,0 +1,712 @@ +const PARSED = [ + // ML-style HOF + { + query: "(-> F

)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(-> F

)", + returned: [], + userQuery: "(-> f

)", + error: null, + }, + { + query: "(-> P)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(-> P)", + returned: [], + userQuery: "(-> p)", + error: null, + }, + { + query: "(->,a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(->,a)", + returned: [], + userQuery: "(->,a)", + error: null, + }, + { + query: "(F

->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(F

->)", + returned: [], + userQuery: "(f

->)", + error: null, + }, + { + query: "(P ->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(P ->)", + returned: [], + userQuery: "(p ->)", + error: null, + }, + { + query: "(,a->)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(,a->)", + returned: [], + userQuery: "(,a->)", + error: null, + }, + { + query: "(aaaaa->a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [{ + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(aaaaa->a)", + returned: [], + userQuery: "(aaaaa->a)", + error: null, + }, + { + query: "(aaaaa, b -> a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(aaaaa, b -> a)", + returned: [], + userQuery: "(aaaaa, b -> a)", + error: null, + }, + { + query: "primitive:(aaaaa, b -> a)", + elems: [{ + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:(aaaaa, b -> a)", + returned: [], + userQuery: "primitive:(aaaaa, b -> a)", + error: null, + }, + { + query: "x, trait:(aaaaa, b -> a)", + elems: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 10, + } + ], + foundElems: 2, + original: "x, trait:(aaaaa, b -> a)", + returned: [], + userQuery: "x, trait:(aaaaa, b -> a)", + error: null, + }, + // Rust-style HOF + { + query: "Fn () -> F

", + elems: [{ + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [], + bindings: [ + [ + "output", + [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "Fn () -> F

", + returned: [], + userQuery: "fn () -> f

", + error: null, + }, + { + query: "FnMut() -> P", + elems: [{ + name: "fnmut", + fullPath: ["fnmut"], + pathWithoutLast: [], + pathLast: "fnmut", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "FnMut() -> P", + returned: [], + userQuery: "fnmut() -> p", + error: null, + }, + { + query: "(FnMut() -> P)", + elems: [{ + name: "fnmut", + fullPath: ["fnmut"], + pathWithoutLast: [], + pathLast: "fnmut", + generics: [], + bindings: [ + [ + "output", + [{ + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "(FnMut() -> P)", + returned: [], + userQuery: "(fnmut() -> p)", + error: null, + }, + { + query: "Fn(F

)", + elems: [{ + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [{ + name: "f", + fullPath: ["f"], + pathWithoutLast: [], + pathLast: "f", + generics: [ + { + name: "p", + fullPath: ["p"], + pathWithoutLast: [], + pathLast: "p", + generics: [], + }, + ], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ], + ], + typeFilter: -1, + }], + foundElems: 1, + original: "Fn(F

)", + returned: [], + userQuery: "fn(f

)", + error: null, + }, + { + query: "primitive:fnonce(aaaaa, b) -> a", + elems: [{ + name: "fnonce", + fullPath: ["fnonce"], + pathWithoutLast: [], + pathLast: "fnonce", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:fnonce(aaaaa, b) -> a", + returned: [], + userQuery: "primitive:fnonce(aaaaa, b) -> a", + error: null, + }, + { + query: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + elems: [{ + name: "fnonce", + fullPath: ["fnonce"], + pathWithoutLast: [], + pathLast: "fnonce", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: 0, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: 10, + }], + ], + ], + typeFilter: 1, + }], + foundElems: 1, + original: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + returned: [], + userQuery: "primitive:fnonce(aaaaa, keyword:b) -> trait:a", + error: null, + }, + { + query: "x, trait:fn(aaaaa, b -> a)", + elems: [ + { + name: "x", + fullPath: ["x"], + pathWithoutLast: [], + pathLast: "x", + generics: [], + typeFilter: -1, + }, + { + name: "fn", + fullPath: ["fn"], + pathWithoutLast: [], + pathLast: "fn", + generics: [ + { + name: "->", + fullPath: ["->"], + pathWithoutLast: [], + pathLast: "->", + generics: [ + { + name: "aaaaa", + fullPath: ["aaaaa"], + pathWithoutLast: [], + pathLast: "aaaaa", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [{ + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }], + ], + ], + typeFilter: -1, + }, + ], + bindings: [ + [ + "output", + [], + ] + ], + typeFilter: 10, + } + ], + foundElems: 2, + original: "x, trait:fn(aaaaa, b -> a)", + returned: [], + userQuery: "x, trait:fn(aaaaa, b -> a)", + error: null, + }, + { + query: 'a,b(c)', + elems: [ + { + name: "a", + fullPath: ["a"], + pathWithoutLast: [], + pathLast: "a", + generics: [], + typeFilter: -1, + }, + { + name: "b", + fullPath: ["b"], + pathWithoutLast: [], + pathLast: "b", + generics: [{ + name: "c", + fullPath: ["c"], + pathWithoutLast: [], + pathLast: "c", + generics: [], + typeFilter: -1, + }], + bindings: [ + [ + "output", + [], + ] + ], + typeFilter: -1, + } + ], + foundElems: 2, + original: "a,b(c)", + returned: [], + userQuery: "a,b(c)", + error: null, + }, +]; diff --git a/tests/rustdoc-js-std/parser-weird-queries.js b/tests/rustdoc-js-std/parser-weird-queries.js index 26b8c32d6805..499b82a34694 100644 --- a/tests/rustdoc-js-std/parser-weird-queries.js +++ b/tests/rustdoc-js-std/parser-weird-queries.js @@ -37,15 +37,6 @@ const PARSED = [ userQuery: "a b", error: null, }, - { - query: 'a,b(c)', - elems: [], - foundElems: 0, - original: "a,b(c)", - returned: [], - userQuery: "a,b(c)", - error: "Expected `,`, `:` or `->`, found `(`", - }, { query: 'aaa,a', elems: [ diff --git a/tests/rustdoc-js/auxiliary/interner.rs b/tests/rustdoc-js/auxiliary/interner.rs new file mode 100644 index 000000000000..c95029be9f0f --- /dev/null +++ b/tests/rustdoc-js/auxiliary/interner.rs @@ -0,0 +1,245 @@ +#![feature(associated_type_defaults)] + +use std::cmp::Ord; +use std::fmt::{Debug, Formatter}; +use std::hash::Hash; +use std::ops::ControlFlow; + +pub trait Interner: Sized { + type DefId: Copy + Debug + Hash + Ord; + type AdtDef: Copy + Debug + Hash + Ord; + type GenericArgs: Copy + + DebugWithInfcx + + Hash + + Ord + + IntoIterator; + type GenericArg: Copy + DebugWithInfcx + Hash + Ord; + type Term: Copy + Debug + Hash + Ord; + type Binder>: BoundVars + TypeSuperVisitable; + type BoundVars: IntoIterator; + type BoundVar; + type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator>; + type Ty: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + TypeSuperVisitable + + Flags + + Ty; + type Tys: Copy + Debug + Hash + Ord + IntoIterator; + type AliasTy: Copy + DebugWithInfcx + Hash + Ord; + type ParamTy: Copy + Debug + Hash + Ord; + type BoundTy: Copy + Debug + Hash + Ord; + type PlaceholderTy: Copy + Debug + Hash + Ord + PlaceholderLike; + type ErrorGuaranteed: Copy + Debug + Hash + Ord; + type BoundExistentialPredicates: Copy + DebugWithInfcx + Hash + Ord; + type PolyFnSig: Copy + DebugWithInfcx + Hash + Ord; + type AllocId: Copy + Debug + Hash + Ord; + type Const: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + ConstTy + + TypeSuperVisitable + + Flags + + Const; + type AliasConst: Copy + DebugWithInfcx + Hash + Ord; + type PlaceholderConst: Copy + Debug + Hash + Ord + PlaceholderLike; + type ParamConst: Copy + Debug + Hash + Ord; + type BoundConst: Copy + Debug + Hash + Ord; + type ValueConst: Copy + Debug + Hash + Ord; + type ExprConst: Copy + DebugWithInfcx + Hash + Ord; + type Region: Copy + + DebugWithInfcx + + Hash + + Ord + + Into + + IntoKind> + + Flags + + Region; + type EarlyParamRegion: Copy + Debug + Hash + Ord; + type LateParamRegion: Copy + Debug + Hash + Ord; + type BoundRegion: Copy + Debug + Hash + Ord; + type InferRegion: Copy + DebugWithInfcx + Hash + Ord; + type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike; + type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable + Flags; + type TraitPredicate: Copy + Debug + Hash + Eq; + type RegionOutlivesPredicate: Copy + Debug + Hash + Eq; + type TypeOutlivesPredicate: Copy + Debug + Hash + Eq; + type ProjectionPredicate: Copy + Debug + Hash + Eq; + type NormalizesTo: Copy + Debug + Hash + Eq; + type SubtypePredicate: Copy + Debug + Hash + Eq; + type CoercePredicate: Copy + Debug + Hash + Eq; + type ClosureKind: Copy + Debug + Hash + Eq; + + // Required method + fn mk_canonical_var_infos( + self, + infos: &[CanonicalVarInfo] + ) -> Self::CanonicalVars; +} + +pub trait DebugWithInfcx: Debug { + // Required method + fn fmt>( + this: WithInfcx<'_, Infcx, &Self>, + f: &mut Formatter<'_> + ) -> std::fmt::Result; +} + +pub trait TypeVisitable: Debug + Clone { + // Required method + fn visit_with>(&self, visitor: &mut V) -> V::Result; +} + +pub trait BoundVars { + // Required methods + fn bound_vars(&self) -> I::BoundVars; + fn has_no_bound_vars(&self) -> bool; +} + +pub trait TypeSuperVisitable: TypeVisitable { + // Required method + fn super_visit_with>(&self, visitor: &mut V) -> V::Result; +} + +pub struct CanonicalVarInfo { + pub kind: CanonicalVarKind, +} + +pub struct CanonicalVarKind(std::marker::PhantomData); + +pub struct TyKind(std::marker::PhantomData); + +pub trait IntoKind { + type Kind; + + // Required method + fn kind(self) -> Self::Kind; +} +pub trait Flags { + // Required methods + fn flags(&self) -> TypeFlags; + fn outer_exclusive_binder(&self) -> DebruijnIndex; +} +pub struct TypeFlags; + +pub trait Ty> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar + ) -> Self; +} + +pub trait PlaceholderLike { + // Required methods + fn universe(self) -> UniverseIndex; + fn var(self) -> BoundVar; + fn with_updated_universe(self, ui: UniverseIndex) -> Self; + fn new(ui: UniverseIndex, var: BoundVar) -> Self; +} + +pub struct UniverseIndex; + +pub struct BoundVar; + +pub struct ConstKind(std::marker::PhantomData); +pub trait Const> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar, + ty: I::Ty + ) -> Self; +} + +pub trait ConstTy { + // Required method + fn ty(self) -> I::Ty; +} + +pub struct DebruijnIndex; + +pub struct RegionKind(std::marker::PhantomData); +pub trait Region> { + // Required method + fn new_anon_bound( + interner: I, + debruijn: DebruijnIndex, + var: BoundVar + ) -> Self; +} + +pub trait TypeVisitor: Sized { + type Result: VisitorResult = (); + + // Provided methods + fn visit_binder>( + &mut self, + t: &I::Binder + ) -> Self::Result { unimplemented!() } + fn visit_ty(&mut self, t: I::Ty) -> Self::Result { unimplemented!() } + fn visit_region(&mut self, _r: I::Region) -> Self::Result { unimplemented!() } + fn visit_const(&mut self, c: I::Const) -> Self::Result { unimplemented!() } + fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result { unimplemented!() } +} + +pub trait VisitorResult { + type Residual; + + // Required methods + fn output() -> Self; + fn from_residual(residual: Self::Residual) -> Self; + fn from_branch(b: ControlFlow) -> Self; + fn branch(self) -> ControlFlow; +} + +impl VisitorResult for () { + type Residual = (); + fn output() -> Self {} + fn from_residual(_: Self::Residual) -> Self {} + fn from_branch(_: ControlFlow) -> Self {} + fn branch(self) -> ControlFlow { ControlFlow::Continue(()) } +} + +pub struct WithInfcx<'a, Infcx: InferCtxtLike, T> { + pub data: T, + pub infcx: &'a Infcx, +} + +pub trait InferCtxtLike { + type Interner: Interner; + + // Required methods + fn interner(&self) -> Self::Interner; + fn universe_of_ty(&self, ty: TyVid) -> Option; + fn root_ty_var(&self, vid: TyVid) -> TyVid; + fn probe_ty_var( + &self, + vid: TyVid + ) -> Option<::Ty>; + fn universe_of_lt( + &self, + lt: ::InferRegion + ) -> Option; + fn opportunistic_resolve_lt_var( + &self, + vid: ::InferRegion + ) -> Option<::Region>; + fn universe_of_ct(&self, ct: ConstVid) -> Option; + fn root_ct_var(&self, vid: ConstVid) -> ConstVid; + fn probe_ct_var( + &self, + vid: ConstVid + ) -> Option<::Const>; +} + +pub struct TyVid; +pub struct ConstVid; diff --git a/tests/rustdoc-js/hof.js b/tests/rustdoc-js/hof.js new file mode 100644 index 000000000000..5e6c9d83c7c7 --- /dev/null +++ b/tests/rustdoc-js/hof.js @@ -0,0 +1,176 @@ +// exact-check + +const EXPECTED = [ + // not a HOF query + { + 'query': 'u32 -> !', + 'others': [], + }, + + // ML-style higher-order function notation + { + 'query': 'bool, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'u8, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'i8, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'char, (u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': '(first -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': '(second -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': '(third -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': '(u32 -> !) -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + {"path": "hof", "name": "fn_ptr"}, + {"path": "hof", "name": "fn_mut"}, + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': '(str, str -> i8) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(str ->) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(-> i8) -> ()', + 'others': [ + {"path": "hof", "name": "multiple"}, + ], + }, + { + 'query': '(str -> str) -> ()', + // params and return are not the same + 'others': [], + }, + { + 'query': '(i8 ->) -> ()', + // params and return are not the same + 'others': [], + }, + { + 'query': '(-> str) -> ()', + // params and return are not the same + 'others': [], + }, + + // Rust-style higher-order function notation + { + 'query': 'bool, fn(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'u8, fnonce(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'u8, fn(u32) -> ! -> ()', + // fnonce != fn + 'others': [], + }, + { + 'query': 'i8, fnmut(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'i8, fn(u32) -> ! -> ()', + // fnmut != fn + 'others': [], + }, + { + 'query': 'char, fn(u32) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': 'char, fnmut(u32) -> ! -> ()', + // fn != fnmut + 'others': [], + }, + { + 'query': 'fn(first) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'fnonce(second) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_once"}, + ], + }, + { + 'query': 'fnmut(third) -> ! -> ()', + 'others': [ + {"path": "hof", "name": "fn_mut"}, + ], + }, + { + 'query': 'fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_"}, + {"path": "hof", "name": "fn_ptr"}, + ], + }, + { + 'query': 'trait:fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_"}, + ], + }, + { + 'query': 'primitive:fn(u32) -> ! -> ()', + 'others': [ + // fn matches primitive:fn and trait:Fn + {"path": "hof", "name": "fn_ptr"}, + ], + }, +]; diff --git a/tests/rustdoc-js/hof.rs b/tests/rustdoc-js/hof.rs new file mode 100644 index 000000000000..4d2c6e331cac --- /dev/null +++ b/tests/rustdoc-js/hof.rs @@ -0,0 +1,12 @@ +#![feature(never_type)] + +pub struct First(T); +pub struct Second(T); +pub struct Third(T); + +pub fn fn_ptr(_: fn (First) -> !, _: bool) {} +pub fn fn_once(_: impl FnOnce (Second) -> !, _: u8) {} +pub fn fn_mut(_: impl FnMut (Third) -> !, _: i8) {} +pub fn fn_(_: impl Fn (u32) -> !, _: char) {} + +pub fn multiple(_: impl Fn(&'static str, &'static str) -> i8) {} diff --git a/tests/rustdoc-js/looks-like-rustc-interner.js b/tests/rustdoc-js/looks-like-rustc-interner.js new file mode 100644 index 000000000000..a4806d234992 --- /dev/null +++ b/tests/rustdoc-js/looks-like-rustc-interner.js @@ -0,0 +1,9 @@ +// https://github.com/rust-lang/rust/pull/122247 +// exact-check + +const EXPECTED = { + 'query': 'canonicalvarinfo, intoiterator -> intoiterator', + 'others': [ + { 'path': 'looks_like_rustc_interner::Interner', 'name': 'mk_canonical_var_infos' }, + ], +}; diff --git a/tests/rustdoc-js/looks-like-rustc-interner.rs b/tests/rustdoc-js/looks-like-rustc-interner.rs new file mode 100644 index 000000000000..f304e28d9524 --- /dev/null +++ b/tests/rustdoc-js/looks-like-rustc-interner.rs @@ -0,0 +1,5 @@ +//@ aux-crate:interner=interner.rs +// https://github.com/rust-lang/rust/pull/122247 +extern crate interner; +#[doc(inline)] +pub use interner::*; diff --git a/tests/ui-fulldeps/stable-mir/check_transform.rs b/tests/ui-fulldeps/stable-mir/check_transform.rs new file mode 100644 index 000000000000..e7d852a27df0 --- /dev/null +++ b/tests/ui-fulldeps/stable-mir/check_transform.rs @@ -0,0 +1,147 @@ +//@ run-pass +//! Test a few methods to transform StableMIR. + +//@ ignore-stage1 +//@ ignore-cross-compile +//@ ignore-remote +//@ ignore-windows-gnu mingw has troubles with linking https://github.com/rust-lang/rust/pull/116837 + +#![feature(rustc_private)] +#![feature(assert_matches)] +#![feature(control_flow_enum)] +#![feature(ascii_char, ascii_char_variants)] + +extern crate rustc_hir; +#[macro_use] +extern crate rustc_smir; +extern crate rustc_driver; +extern crate rustc_interface; +extern crate stable_mir; + +use rustc_smir::rustc_internal; +use stable_mir::mir::alloc::GlobalAlloc; +use stable_mir::mir::mono::Instance; +use stable_mir::mir::{Body, Constant, Operand, Rvalue, StatementKind, TerminatorKind}; +use stable_mir::ty::{Const, ConstantKind}; +use stable_mir::{CrateDef, CrateItems, ItemKind}; +use std::convert::TryFrom; +use std::io::Write; +use std::ops::ControlFlow; + +const CRATE_NAME: &str = "input"; + +/// This function uses the Stable MIR APIs to transform the MIR. +fn test_transform() -> ControlFlow<()> { + // Find items in the local crate. + let items = stable_mir::all_local_items(); + + // Test fn_abi + let target_fn = *get_item(&items, (ItemKind::Fn, "dummy")).unwrap(); + let instance = Instance::try_from(target_fn).unwrap(); + let body = instance.body().unwrap(); + check_msg(&body, "oops"); + + let new_msg = "new panic message"; + let new_body = change_panic_msg(body, new_msg); + check_msg(&new_body, new_msg); + + ControlFlow::Continue(()) +} + +/// Check that the body panic message matches the given message. +fn check_msg(body: &Body, expected: &str) { + let msg = body + .blocks + .iter() + .find_map(|bb| match &bb.terminator.kind { + TerminatorKind::Call { args, .. } => { + assert_eq!(args.len(), 1, "Expected panic message, but found {args:?}"); + let msg_const = match &args[0] { + Operand::Constant(msg_const) => msg_const, + Operand::Copy(place) | Operand::Move(place) => { + assert!(place.projection.is_empty()); + bb.statements + .iter() + .find_map(|stmt| match &stmt.kind { + StatementKind::Assign( + destination, + Rvalue::Use(Operand::Constant(msg_const)), + ) if destination == place => Some(msg_const), + _ => None, + }) + .unwrap() + } + }; + let ConstantKind::Allocated(alloc) = msg_const.literal.kind() else { + unreachable!() + }; + assert_eq!(alloc.provenance.ptrs.len(), 1); + + let alloc_prov_id = alloc.provenance.ptrs[0].1 .0; + let GlobalAlloc::Memory(val) = GlobalAlloc::from(alloc_prov_id) else { + unreachable!() + }; + let bytes = val.raw_bytes().unwrap(); + Some(std::str::from_utf8(&bytes).unwrap().to_string()) + } + _ => None, + }) + .expect("Failed to find panic message"); + assert_eq!(&msg, expected); +} + +/// Modify body to use a different panic message. +fn change_panic_msg(mut body: Body, new_msg: &str) -> Body { + for bb in &mut body.blocks { + match &mut bb.terminator.kind { + TerminatorKind::Call { args, .. } => { + let new_const = Const::from_str(new_msg); + args[0] = Operand::Constant(Constant { + literal: new_const, + span: bb.terminator.span, + user_ty: None, + }); + } + _ => {} + } + } + body +} + +fn get_item<'a>( + items: &'a CrateItems, + item: (ItemKind, &str), +) -> Option<&'a stable_mir::CrateItem> { + items.iter().find(|crate_item| (item.0 == crate_item.kind()) && crate_item.name() == item.1) +} + +/// This test will generate and analyze a dummy crate using the stable mir. +/// For that, it will first write the dummy crate into a file. +/// Then it will create a `StableMir` using custom arguments and then +/// it will run the compiler. +fn main() { + let path = "transform_input.rs"; + generate_input(&path).unwrap(); + let args = vec![ + "rustc".to_string(), + "--crate-type=lib".to_string(), + "--crate-name".to_string(), + CRATE_NAME.to_string(), + path.to_string(), + ]; + run!(args, test_transform).unwrap(); +} + +fn generate_input(path: &str) -> std::io::Result<()> { + let mut file = std::fs::File::create(path)?; + write!( + file, + r#" + #![feature(panic_internals)] + pub fn dummy() {{ + core::panicking::panic_str("oops"); + }} + "# + )?; + Ok(()) +} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-1.rs b/tests/ui/associated-type-bounds/dedup-normalized-1.rs new file mode 100644 index 000000000000..5329018e79f2 --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-1.rs @@ -0,0 +1,24 @@ +//@ check-pass + +// We try to prove `T::Rigid: Into` and have 2 candidates from where-clauses: +// +// - `Into` +// - `Into<::Assoc>` +// +// This causes ambiguity unless we normalize the alias in the second candidate +// to detect that they actually result in the same constraints. +trait Trait { + type Rigid: Elaborate + Into; +} + +trait Elaborate: Into { + type Assoc; +} + +fn impls, U>(_: T) {} + +fn test(rigid: P::Rigid) { + impls(rigid); +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs new file mode 100644 index 000000000000..9224d47d30fd --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.rs @@ -0,0 +1,27 @@ +// We try to prove `for<'b> T::Rigid: Bound<'b, ?0>` and have 2 candidates from where-clauses: +// +// - `for<'a> Bound<'a, String>` +// - `for<'a> Bound<'a, ::Assoc>` +// +// This causes ambiguity unless we normalize the alias in the second candidate +// to detect that they actually result in the same constraints. We currently +// fail to detect that the constraints from these bounds are equal and error +// with ambiguity. +trait Bound<'a, U> {} + +trait Trait { + type Rigid: Elaborate + for<'a> Bound<'a, String>; +} + +trait Elaborate: for<'a> Bound<'a, Self::Assoc> { + type Assoc; +} + +fn impls Bound<'b, U>, U>(_: T) {} + +fn test(rigid: P::Rigid) { + impls(rigid); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr new file mode 100644 index 000000000000..372d379de5a4 --- /dev/null +++ b/tests/ui/associated-type-bounds/dedup-normalized-2-higher-ranked.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed + --> $DIR/dedup-normalized-2-higher-ranked.rs:23:5 + | +LL | impls(rigid); + | ^^^^^ cannot infer type of the type parameter `U` declared on the function `impls` + | + = note: cannot satisfy `for<'b>

::Rigid: Bound<'b, _>` +note: required by a bound in `impls` + --> $DIR/dedup-normalized-2-higher-ranked.rs:20:13 + | +LL | fn impls Bound<'b, U>, U>(_: T) {} + | ^^^^^^^^^^^^^^^^^^^^ required by this bound in `impls` +help: consider specifying the generic arguments + | +LL | impls::<

::Rigid, U>(rigid); + | ++++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/async-await/in-trait/hir-hash.rs b/tests/ui/async-await/in-trait/hir-hash.rs new file mode 100644 index 000000000000..8324fec8282f --- /dev/null +++ b/tests/ui/async-await/in-trait/hir-hash.rs @@ -0,0 +1,11 @@ +// Issue #122508 + +//@ check-pass +//@ incremental +//@ edition:2021 + +trait MyTrait { + async fn bar(&self) -> i32; +} + +fn main() {} diff --git a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr b/tests/ui/async-await/return-type-notation/issue-110963-early.stderr index feae2698e8fa..23ede089b5a8 100644 --- a/tests/ui/async-await/return-type-notation/issue-110963-early.stderr +++ b/tests/ui/async-await/return-type-notation/issue-110963-early.stderr @@ -7,7 +7,7 @@ LL | #![feature(return_type_notation)] = note: see issue #109417 for more information = note: `#[warn(incomplete_features)]` on by default -error[E0308]: mismatched types +error: implementation of `Send` is not general enough --> $DIR/issue-110963-early.rs:14:5 | LL | / spawn(async move { @@ -16,17 +16,12 @@ LL | | if !hc.check().await { LL | | log_health_check_failure().await; LL | | } LL | | }); - | |______^ one type is more general than the other + | |______^ implementation of `Send` is not general enough | - = note: expected trait `Send` - found trait `for<'a> Send` -note: the lifetime requirement is introduced here - --> $DIR/issue-110963-early.rs:34:17 - | -LL | F: Future + Send + 'static, - | ^^^^ + = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>() }`, for any two lifetimes `'0` and `'1`... + = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>() }`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Send` is not general enough --> $DIR/issue-110963-early.rs:14:5 | LL | / spawn(async move { @@ -35,17 +30,11 @@ LL | | if !hc.check().await { LL | | log_health_check_failure().await; LL | | } LL | | }); - | |______^ one type is more general than the other + | |______^ implementation of `Send` is not general enough | - = note: expected trait `Send` - found trait `for<'a> Send` -note: the lifetime requirement is introduced here - --> $DIR/issue-110963-early.rs:34:17 - | -LL | F: Future + Send + 'static, - | ^^^^ + = note: `Send` would have to be implemented for the type `impl Future { ::check<'0>() }`, for any two lifetimes `'0` and `'1`... + = note: ...but `Send` is actually implemented for the type `impl Future { ::check<'2>() }`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 2 previous errors; 1 warning emitted -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/closures/multiple-fn-bounds.stderr b/tests/ui/closures/multiple-fn-bounds.stderr index 9a49fc99ac31..861b39b4d076 100644 --- a/tests/ui/closures/multiple-fn-bounds.stderr +++ b/tests/ui/closures/multiple-fn-bounds.stderr @@ -8,7 +8,7 @@ LL | foo(move |x| v); | expected due to this | = note: expected closure signature `fn(_) -> _` - found closure signature `for<'a> fn(&'a _) -> _` + found closure signature `fn(&_) -> _` note: closure inferred to have a different signature due to this bound --> $DIR/multiple-fn-bounds.rs:1:11 | diff --git a/tests/ui/consts/const-eval/erroneous-const.stderr b/tests/ui/consts/const-eval/erroneous-const.stderr deleted file mode 100644 index bd25e96c2cf1..000000000000 --- a/tests/ui/consts/const-eval/erroneous-const.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const.rs:13:13 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const2.rs b/tests/ui/consts/const-eval/erroneous-const2.rs deleted file mode 100644 index 61f2955f2d82..000000000000 --- a/tests/ui/consts/const-eval/erroneous-const2.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Make sure we error on erroneous consts even if they are unused. -#![allow(unconditional_panic)] - -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed -} - -pub static FOO: () = { - if false { - // This bad constant is only used in dead code in a static initializer... and yet we still - // must make sure that the build fails. - PrintName::::VOID; //~ constant - } -}; - -fn main() { - FOO -} diff --git a/tests/ui/consts/const-eval/erroneous-const2.stderr b/tests/ui/consts/const-eval/erroneous-const2.stderr deleted file mode 100644 index 6a5839e3dfb4..000000000000 --- a/tests/ui/consts/const-eval/erroneous-const2.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/erroneous-const2.rs:6:22 - | -LL | const VOID: () = [()][2]; - | ^^^^^^^ index out of bounds: the length is 1 but the index is 2 - -note: erroneous constant encountered - --> $DIR/erroneous-const2.rs:13:9 - | -LL | PrintName::::VOID; - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.stderr b/tests/ui/consts/const-eval/unused-broken-const-late.stderr deleted file mode 100644 index c2cf2f3813c5..000000000000 --- a/tests/ui/consts/const-eval/unused-broken-const-late.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0080]: evaluation of `PrintName::::VOID` failed - --> $DIR/unused-broken-const-late.rs:8:22 - | -LL | const VOID: () = panic!(); - | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/unused-broken-const-late.rs:8:22 - | - = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr new file mode 100644 index 000000000000..c7ff1328917f --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-called-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/collect-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr new file mode 100644 index 000000000000..c7ff1328917f --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-called-fn.rs:9:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-called-fn.rs:9:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn called::` + --> $DIR/collect-in-called-fn.rs:23:5 + | +LL | called::(); + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/unused-broken-const-late.rs b/tests/ui/consts/required-consts/collect-in-called-fn.rs similarity index 55% rename from tests/ui/consts/const-eval/unused-broken-const-late.rs rename to tests/ui/consts/required-consts/collect-in-called-fn.rs index c4916061f9e5..55133a10cd98 100644 --- a/tests/ui/consts/const-eval/unused-broken-const-late.rs +++ b/tests/ui/consts/required-consts/collect-in-called-fn.rs @@ -1,20 +1,24 @@ +//@revisions: noopt opt //@ build-fail -//@ compile-flags: -O +//@[opt] compile-flags: -O //! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is //! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) -struct PrintName(T); -impl PrintName { - const VOID: () = panic!(); //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } -fn no_codegen() { +#[inline(never)] +fn called() { // Any function that is called is guaranteed to have all consts that syntactically // appear in its body evaluated, even if they only appear in dead code. + // This relies on mono-item collection checking `required_consts` in collected functions. if false { - let _ = PrintName::::VOID; + let _ = Fail::::C; } } + pub fn main() { - no_codegen::(); + called::(); } diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr new file mode 100644 index 000000000000..b7010e787633 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.noopt.stderr @@ -0,0 +1,14 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-drop.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-drop.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-drop.rs b/tests/ui/consts/required-consts/collect-in-dead-drop.rs new file mode 100644 index 000000000000..c9ffcec69031 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-drop.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + // Now it gest dropped implicitly, at the end of this scope. + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr new file mode 100644 index 000000000000..2162c35c8370 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-fn.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-fn.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn not_called::` + --> $DIR/collect-in-dead-fn.rs:29:9 + | +LL | not_called::(); + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-fn.rs b/tests/ui/consts/required-consts/collect-in-dead-fn.rs new file mode 100644 index 000000000000..9e6b15191533 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-fn.rs @@ -0,0 +1,35 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but it is mentioned in dead code in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that syntactically +// are called in collected functions (even inside dead code). +#[inline(never)] +fn not_called() { + if false { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called() { + if false { + not_called::(); + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-forget.rs b/tests/ui/consts/required-consts/collect-in-dead-forget.rs new file mode 100644 index 000000000000..720b7a499f78 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-forget.rs @@ -0,0 +1,32 @@ +//@revisions: noopt opt +//@build-pass +//@[opt] compile-flags: -O +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + std::mem::forget(v); + // Now the destructor never gets "mentioned" so this build should *not* fail. + // IOW, this demonstrates that we are using a post-drop-elab notion of "mentioned". + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr new file mode 100644 index 000000000000..8c853127e044 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.noopt.stderr @@ -0,0 +1,14 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-move.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-move.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as std::ops::Drop>::drop` + --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-move.rs b/tests/ui/consts/required-consts/collect-in-dead-move.rs new file mode 100644 index 000000000000..f3a6ba8a6577 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-move.rs @@ -0,0 +1,33 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +// This function is not actually called, but is mentioned implicitly as destructor in dead code in a +// function that is called. Make sure we still find this error. +impl Drop for Fail { + fn drop(&mut self) { + let _ = Fail::::C; + } +} + +#[inline(never)] +fn called(x: T) { + if false { + let v = Fail(x); + drop(v); // move `v` away (and it then gets dropped there so build still fails) + } +} + +pub fn main() { + called::(0); +} diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr new file mode 100644 index 000000000000..6fd82777bd36 --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/collect-in-dead-vtable.rs:12:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/collect-in-dead-vtable.rs:12:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn as MyTrait>::not_called` + --> $DIR/collect-in-dead-vtable.rs:35:40 + | +LL | let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + | ^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/collect-in-dead-vtable.rs b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs new file mode 100644 index 000000000000..f21a1cc1fc2f --- /dev/null +++ b/tests/ui/consts/required-consts/collect-in-dead-vtable.rs @@ -0,0 +1,41 @@ +//@revisions: noopt opt +//@[noopt] build-fail +//@[opt] compile-flags: -O +//FIXME: `opt` revision currently does not stop with an error due to +//. +//@[opt] build-pass +//! Make sure we detect erroneous constants post-monomorphization even when they are unused. This is +//! crucial, people rely on it for soundness. (https://github.com/rust-lang/rust/issues/112090) + +struct Fail(T); +impl Fail { + const C: () = panic!(); //[noopt]~ERROR evaluation of `Fail::::C` failed +} + +trait MyTrait { + fn not_called(&self); +} + +// This function is not actually called, but it is mentioned in a vtable in a function that is +// called. Make sure we still find this error. +// This relies on mono-item collection checking `required_consts` in functions that are referenced +// in vtables that syntactically appear in collected functions (even inside dead code). +impl MyTrait for Vec { + fn not_called(&self) { + if false { + let _ = Fail::::C; + } + } +} + +#[inline(never)] +fn called() { + if false { + let v: Vec = Vec::new(); + let gen_vtable: &dyn MyTrait = &v; // vtable "appears" here + } +} + +pub fn main() { + called::(); +} diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr new file mode 100644 index 000000000000..75304591b9f0 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-const-called-fn.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-const-called-fn.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr new file mode 100644 index 000000000000..75304591b9f0 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-const-called-fn.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-const-called-fn.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-const-called-fn.rs:16:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/const-eval/erroneous-const.rs b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs similarity index 52% rename from tests/ui/consts/const-eval/erroneous-const.rs rename to tests/ui/consts/required-consts/interpret-in-const-called-fn.rs index 74d44c5259a3..c409fae0bb90 100644 --- a/tests/ui/consts/const-eval/erroneous-const.rs +++ b/tests/ui/consts/required-consts/interpret-in-const-called-fn.rs @@ -1,16 +1,19 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O //! Make sure we error on erroneous consts even if they are unused. -#![allow(unconditional_panic)] -struct PrintName(T); -impl PrintName { - const VOID: () = [()][2]; //~ERROR evaluation of `PrintName::::VOID` failed +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed } +#[inline(never)] const fn no_codegen() { if false { // This bad constant is only used in dead code in a no-codegen function... and yet we still // must make sure that the build fails. - PrintName::::VOID; //~ constant + // This relies on const-eval evaluating all `required_consts` of `const fn`. + Fail::::C; //~ constant } } diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr new file mode 100644 index 000000000000..491131daf8de --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.noopt.stderr @@ -0,0 +1,27 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/hint.rs:LL:COL + | + = note: entering unreachable code + | +note: inside `unreachable_unchecked` + --> $SRC_DIR/core/src/hint.rs:LL:COL +note: inside `ub` + --> $DIR/interpret-in-promoted.rs:6:5 + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `FOO` + --> $DIR/interpret-in-promoted.rs:12:28 + | +LL | let _x: &'static () = &ub(); + | ^^^^ + +note: erroneous constant encountered + --> $DIR/interpret-in-promoted.rs:12:27 + | +LL | let _x: &'static () = &ub(); + | ^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr new file mode 100644 index 000000000000..491131daf8de --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.opt.stderr @@ -0,0 +1,27 @@ +error[E0080]: evaluation of constant value failed + --> $SRC_DIR/core/src/hint.rs:LL:COL + | + = note: entering unreachable code + | +note: inside `unreachable_unchecked` + --> $SRC_DIR/core/src/hint.rs:LL:COL +note: inside `ub` + --> $DIR/interpret-in-promoted.rs:6:5 + | +LL | std::hint::unreachable_unchecked(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: inside `FOO` + --> $DIR/interpret-in-promoted.rs:12:28 + | +LL | let _x: &'static () = &ub(); + | ^^^^ + +note: erroneous constant encountered + --> $DIR/interpret-in-promoted.rs:12:27 + | +LL | let _x: &'static () = &ub(); + | ^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-promoted.rs b/tests/ui/consts/required-consts/interpret-in-promoted.rs new file mode 100644 index 000000000000..9c2cf4e70d3f --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-promoted.rs @@ -0,0 +1,15 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O +//! Make sure we error on erroneous consts even if they are unused. + +const unsafe fn ub() { + std::hint::unreachable_unchecked(); +} + +pub const FOO: () = unsafe { + // Make sure that this gets promoted and then fails to evaluate, and we deal with that + // correctly. + let _x: &'static () = &ub(); //~ erroneous constant +}; + +fn main() {} diff --git a/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr new file mode 100644 index 000000000000..159c9449fc04 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.noopt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-static.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-static.rs:15:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-static.opt.stderr b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr new file mode 100644 index 000000000000..159c9449fc04 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.opt.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `Fail::::C` failed + --> $DIR/interpret-in-static.rs:7:19 + | +LL | const C: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/interpret-in-static.rs:7:19 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: erroneous constant encountered + --> $DIR/interpret-in-static.rs:15:9 + | +LL | Fail::::C; + | ^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/consts/required-consts/interpret-in-static.rs b/tests/ui/consts/required-consts/interpret-in-static.rs new file mode 100644 index 000000000000..559e281b2b03 --- /dev/null +++ b/tests/ui/consts/required-consts/interpret-in-static.rs @@ -0,0 +1,21 @@ +//@revisions: noopt opt +//@[opt] compile-flags: -O +//! Make sure we error on erroneous consts even if they are unused. + +struct Fail(T); +impl Fail { + const C: () = panic!(); //~ERROR evaluation of `Fail::::C` failed +} + +pub static FOO: () = { + if false { + // This bad constant is only used in dead code in a static initializer... and yet we still + // must make sure that the build fails. + // This relies on const-eval evaluating all `required_consts` of the `static` MIR body. + Fail::::C; //~ constant + } +}; + +fn main() { + FOO +} diff --git a/tests/ui/coroutine/resume-arg-late-bound.rs b/tests/ui/coroutine/resume-arg-late-bound.rs index dd6d318afbcd..3c2ab41047e3 100644 --- a/tests/ui/coroutine/resume-arg-late-bound.rs +++ b/tests/ui/coroutine/resume-arg-late-bound.rs @@ -13,5 +13,5 @@ fn main() { *arg = true; }; test(gen); - //~^ ERROR mismatched types + //~^ ERROR implementation of `Coroutine` is not general enough } diff --git a/tests/ui/coroutine/resume-arg-late-bound.stderr b/tests/ui/coroutine/resume-arg-late-bound.stderr index a97cc6190fd9..4a4ee08c529e 100644 --- a/tests/ui/coroutine/resume-arg-late-bound.stderr +++ b/tests/ui/coroutine/resume-arg-late-bound.stderr @@ -1,17 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `Coroutine` is not general enough --> $DIR/resume-arg-late-bound.rs:15:5 | LL | test(gen); - | ^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^ implementation of `Coroutine` is not general enough | - = note: expected trait `for<'a> Coroutine<&'a mut bool>` - found trait `Coroutine<&mut bool>` -note: the lifetime requirement is introduced here - --> $DIR/resume-arg-late-bound.rs:8:17 - | -LL | fn test(a: impl for<'a> Coroutine<&'a mut bool>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: `{coroutine@$DIR/resume-arg-late-bound.rs:11:15: 11:31}` must implement `Coroutine<&'1 mut bool>`, for any lifetime `'1`... + = note: ...but it actually implements `Coroutine<&'2 mut bool>`, for some specific lifetime `'2` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs b/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs new file mode 100644 index 000000000000..1d946a14aff1 --- /dev/null +++ b/tests/ui/diagnostic_namespace/deny_malformed_attribute.rs @@ -0,0 +1,7 @@ +#![deny(unknown_or_malformed_diagnostic_attributes)] + +#[diagnostic::unknown_attribute] +//~^ERROR unknown diagnostic attribute +struct Foo; + +fn main() {} diff --git a/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr new file mode 100644 index 000000000000..a646d3613de7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/deny_malformed_attribute.stderr @@ -0,0 +1,14 @@ +error: unknown diagnostic attribute + --> $DIR/deny_malformed_attribute.rs:3:15 + | +LL | #[diagnostic::unknown_attribute] + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/deny_malformed_attribute.rs:1:9 + | +LL | #![deny(unknown_or_malformed_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr new file mode 100644 index 000000000000..06ffff057f90 --- /dev/null +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr @@ -0,0 +1,48 @@ +error[E0283]: type annotations needed + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` + | + = note: cannot satisfy `_: Scalar` +note: required by a bound in `cmp_eq` + --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + | +LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + | ^^^^^^ required by this bound in `cmp_eq` +help: consider specifying the generic arguments + | +LL | cmp_eq:: + | +++++++++++ + +error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ + +error[E0275]: overflow evaluating the requirement `impl for<'a, 'b> Fn(::RefType<'a>, ::RefType<'b>) -> O == for<'a, 'b> fn(..., ...) -> ... {cmp_eq::<..., ..., ...>}` + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0275]: overflow evaluating the requirement `for<'a, 'b> fn(::RefType<'a>, <_ as Scalar>::RefType<'b>) -> _ {cmp_eq::} <: ...` + --> $DIR/ambig-hr-projection-issue-93340.rs:14:51 + | +LL | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { + | ___________________________________________________^ +LL | | +LL | | cmp_eq +LL | | +LL | | +LL | | +LL | | } + | |_^ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0275, E0283. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr new file mode 100644 index 000000000000..df2ec4ab182a --- /dev/null +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr @@ -0,0 +1,20 @@ +error[E0283]: type annotations needed + --> $DIR/ambig-hr-projection-issue-93340.rs:16:5 + | +LL | cmp_eq + | ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq` + | + = note: cannot satisfy `_: Scalar` +note: required by a bound in `cmp_eq` + --> $DIR/ambig-hr-projection-issue-93340.rs:9:22 + | +LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + | ^^^^^^ required by this bound in `cmp_eq` +help: consider specifying the generic arguments + | +LL | cmp_eq:: + | +++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs new file mode 100644 index 000000000000..4d8ea9d8d481 --- /dev/null +++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs @@ -0,0 +1,22 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +pub trait Scalar: 'static { + type RefType<'a>: ScalarRef<'a>; +} + +pub trait ScalarRef<'a>: 'a {} + +fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O { + todo!() +} + +fn build_expression( +) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { + //[next]~^ ERROR overflow evaluating the requirement + cmp_eq + //~^ ERROR type annotations needed + //[next]~| ERROR overflow evaluating the requirement + //[next]~| ERROR overflow evaluating the requirement +} + +fn main() {} diff --git a/tests/ui/generic-associated-types/bugs/issue-88382.stderr b/tests/ui/generic-associated-types/bugs/issue-88382.stderr index 9b061528e3be..0f5e394ab615 100644 --- a/tests/ui/generic-associated-types/bugs/issue-88382.stderr +++ b/tests/ui/generic-associated-types/bugs/issue-88382.stderr @@ -1,26 +1,21 @@ -error[E0631]: type mismatch in function arguments +error[E0283]: type annotations needed --> $DIR/issue-88382.rs:26:40 | LL | do_something(SomeImplementation(), test); - | ------------ ^^^^ expected due to this - | | - | required by a bound introduced by this call -... + | ^^^^ cannot infer type of the type parameter `I` declared on the function `test` + | + = note: cannot satisfy `_: Iterable` + = help: the trait `Iterable` is implemented for `SomeImplementation` +note: required by a bound in `test` + --> $DIR/issue-88382.rs:29:16 + | LL | fn test<'a, I: Iterable>(_: &mut I::Iterator<'a>) {} - | ------------------------------------------------- found signature defined here + | ^^^^^^^^ required by this bound in `test` +help: consider specifying the generic argument | - = note: expected function signature `for<'a> fn(&'a mut std::iter::Empty) -> _` - found function signature `for<'a, 'b> fn(&'b mut <_ as Iterable>::Iterator<'a>) -> _` -note: required by a bound in `do_something` - --> $DIR/issue-88382.rs:20:48 - | -LL | fn do_something(i: I, mut f: impl for<'a> Fn(&mut I::Iterator<'a>)) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `do_something` -help: consider wrapping the function in a closure - | -LL | do_something(SomeImplementation(), |arg0: &mut std::iter::Empty| test(/* &mut <_ as Iterable>::Iterator<'_> */)); - | ++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++++ +LL | do_something(SomeImplementation(), test::); + | +++++ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0631`. +For more information about this error, try `rustc --explain E0283`. diff --git a/tests/ui/generic-associated-types/issue-93340.rs b/tests/ui/generic-associated-types/rigid-hr-projection-issue-93340.rs similarity index 80% rename from tests/ui/generic-associated-types/issue-93340.rs rename to tests/ui/generic-associated-types/rigid-hr-projection-issue-93340.rs index 783f8c06ebfd..b55ca845cd3f 100644 --- a/tests/ui/generic-associated-types/issue-93340.rs +++ b/tests/ui/generic-associated-types/rigid-hr-projection-issue-93340.rs @@ -1,3 +1,5 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver //@ check-pass pub trait Scalar: 'static { @@ -12,7 +14,7 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT fn build_expression( ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O { - cmp_eq + cmp_eq:: } fn main() {} diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs index aee5db836694..f89a37c85121 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.rs @@ -10,5 +10,5 @@ fn id(x: &String) -> &String { fn main() { assert_all::<_, &String>(id); - //~^ mismatched types + //~^ ERROR implementation of `FnMut` is not general enough } diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr index c25e731d9627..d7add865aa06 100644 --- a/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-error.stderr @@ -1,12 +1,11 @@ -error[E0308]: mismatched types +error: implementation of `FnMut` is not general enough --> $DIR/higher-ranked-lifetime-error.rs:12:5 | LL | assert_all::<_, &String>(id); - | ^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnMut` is not general enough | - = note: expected trait `for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` - found trait `for<'a> fn(&'a String) -> &'a String {id} as FnMut<(&'a String,)>>` + = note: `for<'a> fn(&'a String) -> &'a String {id}` must implement `FnMut<(&String,)>` + = note: ...but it actually implements `FnMut<(&'0 String,)>`, for some specific lifetime `'0` error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs index 387c78a802a7..4e722dc0e800 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.rs +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.rs @@ -6,17 +6,17 @@ // an error, but the regression test is here to ensure // that it does not ICE. See discussion on #74889 for details. -pub trait T { +pub trait Trait { fn t(&self, _: F) {} } pub fn crash(v: &V) where - for<'a> &'a V: T + 'static, + for<'a> &'a V: Trait + 'static, { v.t(|| {}); - //~^ ERROR: higher-ranked lifetime error - //~| ERROR: higher-ranked lifetime error + //~^ ERROR: implementation of `Trait` is not general enough + //~| ERROR: implementation of `Trait` is not general enough //~| ERROR: higher-ranked lifetime error } diff --git a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr index 3053a2998026..f8bed86ccf59 100644 --- a/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr +++ b/tests/ui/higher-ranked/trait-bounds/issue-59311.stderr @@ -1,18 +1,20 @@ -error: higher-ranked lifetime error +error: implementation of `Trait` is not general enough --> $DIR/issue-59311.rs:17:5 | LL | v.t(|| {}); - | ^^^^^^^^^^ + | ^^^^^^^^^^ implementation of `Trait` is not general enough | - = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed` + = note: `Trait` would have to be implemented for the type `&'a V` + = note: ...but `Trait` is actually implemented for the type `&'0 V`, for some specific lifetime `'0` -error: higher-ranked lifetime error +error: implementation of `Trait` is not general enough --> $DIR/issue-59311.rs:17:5 | LL | v.t(|| {}); - | ^^^^^^^^^^ + | ^^^^^^^^^^ implementation of `Trait` is not general enough | - = note: could not prove `{closure@$DIR/issue-59311.rs:17:9: 17:11} well-formed` + = note: `Trait` would have to be implemented for the type `&'a V` + = note: ...but `Trait` is actually implemented for the type `&'0 V`, for some specific lifetime `'0` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked lifetime error diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs index 4bd3b96e4758..a44ed9e5ef59 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs @@ -43,9 +43,9 @@ fn baz<'a>(s: &'a str) -> (&'a str, Wrapper<'a>) { } foo(bar, "string", |s| s.len() == 5); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough foo(baz, "string", |s| s.0.len() == 5); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr index 1cf364aa9f6a..b2bb417a8f01 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr @@ -1,79 +1,40 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a &'b str)` - found trait `for<'a> FnOnce(&'a &str)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:45:24 - | -LL | foo(bar, "string", |s| s.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:9 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a &'b str)` - found trait `for<'a> FnOnce(&'a &str)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:45:24 - | -LL | foo(bar, "string", |s| s.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:44 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^ + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a Wrapper<'b>)` - found trait `for<'a> FnOnce(&'a Wrapper<'_>)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:48:24 - | -LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:9 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a Wrapper<'b>)` - found trait `for<'a> FnOnce(&'a Wrapper<'_>)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-71955.rs:48:24 - | -LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-71955.rs:25:44 - | -LL | F2: FnOnce(&::Output) -> bool - | ^^^^ + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/implied-bounds/gluon_salsa.rs b/tests/ui/implied-bounds/gluon_salsa.rs index 368fb1979098..cc6352c4a32e 100644 --- a/tests/ui/implied-bounds/gluon_salsa.rs +++ b/tests/ui/implied-bounds/gluon_salsa.rs @@ -1,5 +1,6 @@ //@ check-pass -// Found in a crater run on #118553 +// Related to Bevy regression #115559, found in +// a crater run on #118553. pub trait QueryBase { type Db; @@ -17,11 +18,17 @@ pub struct QueryTable<'me, Q, DB> { _marker: Option<&'me ()>, } -impl<'me, Q> QueryTable<'me, Q, ::Db> // projection is important -// ^^^ removing 'me (and in QueryTable) gives a different error +impl<'me, Q> QueryTable<'me, Q, ::Db> where Q: for<'f> AsyncQueryFunction<'f>, { + // When borrowchechking this function we normalize `::Db` in the + // function signature to `>::SendDb`, where `'?x` is an + // unconstrained region variable. We then addd `>::SendDb: 'a` + // as an implied bound. We currently a structural equality to decide whether this bound + // should be used to prove the bound `>::SendDb: 'a`. For this + // to work we may have to structurally resolve regions as the actually used vars may + // otherwise be semantically equal but structurally different. pub fn get_async<'a>(&'a mut self) { panic!(); } diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs index 58d8be8b65f7..2e2eaca0d335 100644 --- a/tests/ui/lifetimes/issue-105675.rs +++ b/tests/ui/lifetimes/issue-105675.rs @@ -3,12 +3,12 @@ fn thing(x: impl FnOnce(&u32, &u32, u32)) {} fn main() { let f = | _ , y: &u32 , z | (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~^^ ERROR implementation of `FnOnce` is not general enough let f = | x, y: _ , z: u32 | (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR mismatched types - //~^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^ ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/lifetimes/issue-105675.stderr b/tests/ui/lifetimes/issue-105675.stderr index f1fa5a598606..4b3d0e8ac5ef 100644 --- a/tests/ui/lifetimes/issue-105675.stderr +++ b/tests/ui/lifetimes/issue-105675.stderr @@ -1,91 +1,21 @@ -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:5:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other + | ^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `for<'a> FnOnce(&u32, &'a u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:4:13 - | -LL | let f = | _ , y: &u32 , z | (); - | ^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters - | -LL | let f = |_: &_, y: &u32, z| (); - | ~~~~~~~~~~~~~~~~~~~ + = note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:5:5 | LL | thing(f); - | ^^^^^^^^ one type is more general than the other + | ^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `for<'a> FnOnce(&u32, &'a u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:4:13 - | -LL | let f = | _ , y: &u32 , z | (); - | ^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0308]: mismatched types - --> $DIR/issue-105675.rs:9:5 - | -LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `FnOnce(&u32, &u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:8:13 - | -LL | let f = | x, y: _ , z: u32 | (); - | ^^^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters - | -LL | let f = |x: &_, y: &_, z: u32| (); - | ~~~~~~~~~~~~~~~~~~~~~~ - -error[E0308]: mismatched types - --> $DIR/issue-105675.rs:9:5 - | -LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a, 'b> FnOnce(&'a u32, &'b u32, u32)` - found trait `FnOnce(&u32, &u32, u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-105675.rs:8:13 - | -LL | let f = | x, y: _ , z: u32 | (); - | ^^^^^^^^^^^^^^^^^^^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-105675.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^ + = note: closure with signature `for<'a> fn(&'2 u32, &'a u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider specifying the type of the closure parameters - | -LL | let f = |x: &_, y: &_, z: u32| (); - | ~~~~~~~~~~~~~~~~~~~~~~ error: implementation of `FnOnce` is not general enough --> $DIR/issue-105675.rs:9:5 @@ -105,6 +35,25 @@ LL | thing(f); = note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2` +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-105675.rs:9:5 + | +LL | thing(f); + | ^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-105675.rs:9:5 + | +LL | thing(f); + | ^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lifetimes/issue-79187-2.rs b/tests/ui/lifetimes/issue-79187-2.rs index fff92c30b375..9d7f17e7f238 100644 --- a/tests/ui/lifetimes/issue-79187-2.rs +++ b/tests/ui/lifetimes/issue-79187-2.rs @@ -7,7 +7,7 @@ fn take_foo(_: impl Foo) {} fn main() { take_foo(|a| a); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough take_foo(|a: &i32| a); //~^ ERROR lifetime may not live long enough //~| ERROR mismatched types diff --git a/tests/ui/lifetimes/issue-79187-2.stderr b/tests/ui/lifetimes/issue-79187-2.stderr index e8115bb6b064..78f6ce882dfa 100644 --- a/tests/ui/lifetimes/issue-79187-2.stderr +++ b/tests/ui/lifetimes/issue-79187-2.stderr @@ -25,28 +25,14 @@ LL | take_foo(|a| a); = note: closure with signature `fn(&'2 i32) -> &i32` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/issue-79187-2.rs:8:5 | LL | take_foo(|a| a); - | ^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a i32)` - found trait `Fn(&i32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-79187-2.rs:8:14 - | -LL | take_foo(|a| a); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-79187-2.rs:5:21 - | -LL | fn take_foo(_: impl Foo) {} - | ^^^ -help: consider specifying the type of the closure parameters - | -LL | take_foo(|a: &_| a); - | ~~~~~~~ + = note: closure with signature `fn(&'2 i32) -> &i32` must implement `Fn<(&'1 i32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 i32,)>`, for some specific lifetime `'2` error[E0308]: mismatched types --> $DIR/issue-79187-2.rs:11:5 diff --git a/tests/ui/lifetimes/issue-79187.rs b/tests/ui/lifetimes/issue-79187.rs index 8e13045623b3..a8829bd4e490 100644 --- a/tests/ui/lifetimes/issue-79187.rs +++ b/tests/ui/lifetimes/issue-79187.rs @@ -3,6 +3,6 @@ fn thing(x: impl FnOnce(&u32)) {} fn main() { let f = |_| (); thing(f); - //~^ ERROR mismatched types - //~^^ ERROR implementation of `FnOnce` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/lifetimes/issue-79187.stderr b/tests/ui/lifetimes/issue-79187.stderr index 14bdfe75c08b..8adde8d6dfbf 100644 --- a/tests/ui/lifetimes/issue-79187.stderr +++ b/tests/ui/lifetimes/issue-79187.stderr @@ -1,26 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/issue-79187.rs:5:5 - | -LL | thing(f); - | ^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> FnOnce(&'a u32)` - found trait `FnOnce(&u32)` -note: this closure does not fulfill the lifetime requirements - --> $DIR/issue-79187.rs:4:13 - | -LL | let f = |_| (); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/issue-79187.rs:1:18 - | -LL | fn thing(x: impl FnOnce(&u32)) {} - | ^^^^^^^^^^^^ -help: consider specifying the type of the closure parameters - | -LL | let f = |_: &_| (); - | ~~~~~~~ - error: implementation of `FnOnce` is not general enough --> $DIR/issue-79187.rs:5:5 | @@ -30,6 +7,15 @@ LL | thing(f); = note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2` +error: implementation of `FnOnce` is not general enough + --> $DIR/issue-79187.rs:5:5 + | +LL | thing(f); + | ^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs index f17e0a678c96..b02e38bec3b3 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.rs +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.rs @@ -13,6 +13,6 @@ fn g(data: &[T]) { //~^ ERROR the parameter type //~| ERROR the parameter type //~| ERROR the parameter type - //~| ERROR mismatched types //~| ERROR implementation of `FnOnce` is not general + //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr index beb838d2ff83..4dada6ff014a 100644 --- a/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr +++ b/tests/ui/lifetimes/lifetime-errors/issue_74400.stderr @@ -42,19 +42,14 @@ help: consider adding an explicit lifetime bound LL | fn g(data: &[T]) { | +++++++++ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/issue_74400.rs:12:5 | LL | f(data, identity) - | ^^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a T)` - found trait `Fn(&T)` -note: the lifetime requirement is introduced here - --> $DIR/issue_74400.rs:8:34 - | -LL | fn f(data: &[T], key: impl Fn(&T) -> S) { - | ^^^^^^^^^^^ + = note: `fn(&'2 T) -> &'2 T {identity::<&'2 T>}` must implement `Fn<(&'1 T,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 T,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/issue_74400.rs:12:5 @@ -67,5 +62,4 @@ LL | f(data, identity) error: aborting due to 5 previous errors -Some errors have detailed explanations: E0308, E0310. -For more information about an error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/lint/lint-qualification.fixed b/tests/ui/lint/lint-qualification.fixed index 2b1f8b591752..6fe6ba2792fe 100644 --- a/tests/ui/lint/lint-qualification.fixed +++ b/tests/ui/lint/lint-qualification.fixed @@ -1,5 +1,6 @@ //@ run-rustfix #![deny(unused_qualifications)] +#![deny(unused_imports)] #![allow(deprecated, dead_code)] mod foo { @@ -21,8 +22,10 @@ fn main() { //~^ ERROR: unnecessary qualification //~| ERROR: unnecessary qualification - use std::fmt; - let _: fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + + //~^ ERROR: unused import: `std::fmt` + let _: std::fmt::Result = Ok(()); + // don't report unnecessary qualification because fix(#122373) for issue #121331 let _ = ::default(); // issue #121999 //~^ ERROR: unnecessary qualification diff --git a/tests/ui/lint/lint-qualification.rs b/tests/ui/lint/lint-qualification.rs index 002fdbf7724a..19d339b006c9 100644 --- a/tests/ui/lint/lint-qualification.rs +++ b/tests/ui/lint/lint-qualification.rs @@ -1,5 +1,6 @@ //@ run-rustfix #![deny(unused_qualifications)] +#![deny(unused_imports)] #![allow(deprecated, dead_code)] mod foo { @@ -22,7 +23,9 @@ fn main() { //~| ERROR: unnecessary qualification use std::fmt; - let _: std::fmt::Result = Ok(()); //~ ERROR: unnecessary qualification + //~^ ERROR: unused import: `std::fmt` + let _: std::fmt::Result = Ok(()); + // don't report unnecessary qualification because fix(#122373) for issue #121331 let _ = ::default(); // issue #121999 //~^ ERROR: unnecessary qualification diff --git a/tests/ui/lint/lint-qualification.stderr b/tests/ui/lint/lint-qualification.stderr index 8dddcf23f757..9e5c9b2df13f 100644 --- a/tests/ui/lint/lint-qualification.stderr +++ b/tests/ui/lint/lint-qualification.stderr @@ -1,5 +1,5 @@ error: unnecessary qualification - --> $DIR/lint-qualification.rs:11:5 + --> $DIR/lint-qualification.rs:12:5 | LL | foo::bar(); | ^^^^^^^^ @@ -16,7 +16,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:12:5 + --> $DIR/lint-qualification.rs:13:5 | LL | crate::foo::bar(); | ^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:17:13 + --> $DIR/lint-qualification.rs:18:13 | LL | let _ = std::string::String::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = String::new(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:18:13 + --> $DIR/lint-qualification.rs:19:13 | LL | let _ = ::std::env::current_dir(); | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + let _ = std::env::current_dir(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:20:12 + --> $DIR/lint-qualification.rs:21:12 | LL | let _: std::vec::Vec = std::vec::Vec::::new(); | ^^^^^^^^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL + let _: Vec = std::vec::Vec::::new(); | error: unnecessary qualification - --> $DIR/lint-qualification.rs:20:36 + --> $DIR/lint-qualification.rs:21:36 | LL | let _: std::vec::Vec = std::vec::Vec::::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,20 +75,20 @@ LL - let _: std::vec::Vec = std::vec::Vec::::new(); LL + let _: std::vec::Vec = Vec::::new(); | -error: unnecessary qualification - --> $DIR/lint-qualification.rs:25:12 +error: unused import: `std::fmt` + --> $DIR/lint-qualification.rs:25:9 | -LL | let _: std::fmt::Result = Ok(()); - | ^^^^^^^^^^^^^^^^ +LL | use std::fmt; + | ^^^^^^^^ | -help: remove the unnecessary path segments - | -LL - let _: std::fmt::Result = Ok(()); -LL + let _: fmt::Result = Ok(()); +note: the lint level is defined here + --> $DIR/lint-qualification.rs:3:9 | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ error: unnecessary qualification - --> $DIR/lint-qualification.rs:27:13 + --> $DIR/lint-qualification.rs:30:13 | LL | let _ = ::default(); // issue #121999 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed new file mode 100644 index 000000000000..d554bbfcc98c --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.fixed @@ -0,0 +1,50 @@ +//@ run-rustfix +//@ edition:2021 +#![deny(unused_qualifications)] +#![deny(unused_imports)] +#![feature(coroutines, coroutine_trait)] + +use std::ops::{ + Coroutine, + CoroutineState::{self}, + //~^ ERROR unused import: `*` +}; +use std::pin::Pin; + +#[allow(dead_code)] +fn finish(mut amt: usize, mut t: T) -> T::Return + where T: Coroutine<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + CoroutineState::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } +} + + +mod foo { + pub fn bar() {} +} + +pub fn main() { + + use foo::bar; + bar(); + //~^ ERROR unnecessary qualification + bar(); + + // The item `use std::string::String` is imported redundantly. + // Suppress `unused_imports` reporting, otherwise the fixed file will report an error + #[allow(unused_imports)] + use std::string::String; + let s = String::new(); + let y = std::string::String::new(); + // unnecessary qualification + println!("{} {}", s, y); + +} diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs new file mode 100644 index 000000000000..4d79f5ab7453 --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.rs @@ -0,0 +1,50 @@ +//@ run-rustfix +//@ edition:2021 +#![deny(unused_qualifications)] +#![deny(unused_imports)] +#![feature(coroutines, coroutine_trait)] + +use std::ops::{ + Coroutine, + CoroutineState::{self, *}, + //~^ ERROR unused import: `*` +}; +use std::pin::Pin; + +#[allow(dead_code)] +fn finish(mut amt: usize, mut t: T) -> T::Return + where T: Coroutine<(), Yield = ()> + Unpin, +{ + loop { + match Pin::new(&mut t).resume(()) { + CoroutineState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), + CoroutineState::Complete(ret) => { + assert_eq!(amt, 0); + return ret + } + } + } +} + + +mod foo { + pub fn bar() {} +} + +pub fn main() { + + use foo::bar; + foo::bar(); + //~^ ERROR unnecessary qualification + bar(); + + // The item `use std::string::String` is imported redundantly. + // Suppress `unused_imports` reporting, otherwise the fixed file will report an error + #[allow(unused_imports)] + use std::string::String; + let s = String::new(); + let y = std::string::String::new(); + // unnecessary qualification + println!("{} {}", s, y); + +} diff --git a/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr new file mode 100644 index 000000000000..52ed13ea150d --- /dev/null +++ b/tests/ui/lint/unnecessary-qualification/lint-unnecessary-qualification-issue-121331.stderr @@ -0,0 +1,31 @@ +error: unused import: `*` + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:9:28 + | +LL | CoroutineState::{self, *}, + | ^ + | +note: the lint level is defined here + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:4:9 + | +LL | #![deny(unused_imports)] + | ^^^^^^^^^^^^^^ + +error: unnecessary qualification + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:37:5 + | +LL | foo::bar(); + | ^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/lint-unnecessary-qualification-issue-121331.rs:3:9 + | +LL | #![deny(unused_qualifications)] + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove the unnecessary path segments + | +LL - foo::bar(); +LL + bar(); + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr index 452cba6b4de7..e52e095e9f72 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.stderr @@ -24,7 +24,7 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); | expected due to this | = note: expected closure signature `for<'a> fn(&'a {integer}) -> _` - found closure signature `for<'a, 'b, 'c> fn(&'a &'b &'c i32) -> _` + found closure signature `fn(&&&i32) -> _` note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider adjusting the signature so it does not borrow its argument diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch.rs index e73a33dfded7..55a4a0b5707f 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch.rs @@ -8,7 +8,7 @@ fn main() { fn baz(_: F) {} fn _test<'a>(f: fn(*mut &'a u32)) { baz(f); - //~^ ERROR: mismatched types + //~^ ERROR: implementation of `FnOnce` is not general enough //~| ERROR: borrowed data escapes //~| ERROR: not general enough } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr index e63d3f6a075d..abc5d150a3f9 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch.stderr @@ -24,7 +24,7 @@ LL | a.iter().map(|_: &(u16, u16)| 45); | expected due to this | = note: expected closure signature `fn(&(u32, u32)) -> _` - found closure signature `for<'a> fn(&'a (u16, u16)) -> _` + found closure signature `fn(&(u16, u16)) -> _` note: required by a bound in `map` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL @@ -63,19 +63,14 @@ note: due to current limitations in the borrow checker, this implies a `'static` LL | fn baz(_: F) {} | ^^^^^^^^^^^^^ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-arg-type-mismatch.rs:10:5 | LL | baz(f); - | ^^^^^^ one type is more general than the other + | ^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(*mut &'a u32)` - found trait `Fn(*mut &u32)` -note: the lifetime requirement is introduced here - --> $DIR/closure-arg-type-mismatch.rs:8:11 - | -LL | fn baz(_: F) {} - | ^^^^^^^^^^^^^ + = note: `fn(*mut &'2 u32)` must implement `Fn<(*mut &'1 u32,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(*mut &'2 u32,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/closure-arg-type-mismatch.rs:10:5 @@ -88,5 +83,5 @@ LL | baz(f); error: aborting due to 6 previous errors -Some errors have detailed explanations: E0308, E0521, E0631. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0521, E0631. +For more information about an error, try `rustc --explain E0521`. diff --git a/tests/ui/mismatched_types/closure-mismatch.rs b/tests/ui/mismatched_types/closure-mismatch.rs index 4eb33497c395..efaed4dc1b99 100644 --- a/tests/ui/mismatched_types/closure-mismatch.rs +++ b/tests/ui/mismatched_types/closure-mismatch.rs @@ -7,8 +7,8 @@ fn baz(_: T) {} fn main() { baz(|_| ()); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough baz(|x| ()); //~^ ERROR implementation of `FnOnce` is not general enough - //~| ERROR mismatched types + //~| ERROR implementation of `Fn` is not general enough } diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr index 74033c185737..802110c6511d 100644 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-mismatch.stderr @@ -7,28 +7,14 @@ LL | baz(|_| ()); = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-mismatch.rs:8:5 | LL | baz(|_| ()); - | ^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a ())` - found trait `Fn(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/closure-mismatch.rs:8:9 - | -LL | baz(|_| ()); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/closure-mismatch.rs:5:11 - | -LL | fn baz(_: T) {} - | ^^^ -help: consider specifying the type of the closure parameters - | -LL | baz(|_: &_| ()); - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/closure-mismatch.rs:11:5 @@ -39,29 +25,14 @@ LL | baz(|x| ()); = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/closure-mismatch.rs:11:5 | LL | baz(|x| ()); - | ^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a> Fn(&'a ())` - found trait `Fn(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/closure-mismatch.rs:11:9 - | -LL | baz(|x| ()); - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/closure-mismatch.rs:5:11 - | -LL | fn baz(_: T) {} - | ^^^ -help: consider specifying the type of the closure parameters - | -LL | baz(|x: &_| ()); - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2` error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/mismatched_types/fn-variance-1.stderr b/tests/ui/mismatched_types/fn-variance-1.stderr index fdb2e6f0097a..ed450d8d81c8 100644 --- a/tests/ui/mismatched_types/fn-variance-1.stderr +++ b/tests/ui/mismatched_types/fn-variance-1.stderr @@ -10,7 +10,7 @@ LL | apply(&3, takes_mut); | required by a bound introduced by this call | = note: expected function signature `fn(&{integer}) -> _` - found function signature `for<'a> fn(&'a mut isize) -> _` + found function signature `fn(&mut isize) -> _` note: required by a bound in `apply` --> $DIR/fn-variance-1.rs:5:37 | @@ -33,7 +33,7 @@ LL | apply(&mut 3, takes_imm); | required by a bound introduced by this call | = note: expected function signature `fn(&mut {integer}) -> _` - found function signature `for<'a> fn(&'a isize) -> _` + found function signature `fn(&isize) -> _` note: required by a bound in `apply` --> $DIR/fn-variance-1.rs:5:37 | diff --git a/tests/ui/mismatched_types/issue-36053-2.stderr b/tests/ui/mismatched_types/issue-36053-2.stderr index 6d23319ca7e6..ffaa276b62e4 100644 --- a/tests/ui/mismatched_types/issue-36053-2.stderr +++ b/tests/ui/mismatched_types/issue-36053-2.stderr @@ -7,7 +7,7 @@ LL | once::<&str>("str").fuse().filter(|a: &str| true).count(); | expected due to this | = note: expected closure signature `for<'a> fn(&'a &_) -> _` - found closure signature `for<'a> fn(&'a _) -> _` + found closure signature `fn(&_) -> _` note: required by a bound in `filter` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL help: consider adjusting the signature so it borrows its argument diff --git a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr index 0ed57466e9c5..0b8943898f50 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-inference-var.stderr @@ -10,7 +10,7 @@ LL | let _has_inference_vars: Option = Some(0).map(deref_int); | required by a bound introduced by this call | = note: expected function signature `fn({integer}) -> _` - found function signature `for<'a> fn(&'a i32) -> _` + found function signature `fn(&i32) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr index 1ac057a5f382..99c9028ae1ef 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef-unfixable.stderr @@ -10,7 +10,7 @@ LL | let _ = produces_string().and_then(takes_str_but_too_many_refs); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a, 'b> fn(&'a &'b str) -> _` + found function signature `fn(&&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -69,7 +69,7 @@ LL | let _ = Some(TypeWithoutDeref).and_then(takes_str_but_too_many_refs); | required by a bound introduced by this call | = note: expected function signature `fn(TypeWithoutDeref) -> _` - found function signature `for<'a, 'b> fn(&'a &'b str) -> _` + found function signature `fn(&&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/mismatched_types/suggest-option-asderef.stderr b/tests/ui/mismatched_types/suggest-option-asderef.stderr index 1702a7f1decf..306c412e9d24 100644 --- a/tests/ui/mismatched_types/suggest-option-asderef.stderr +++ b/tests/ui/mismatched_types/suggest-option-asderef.stderr @@ -10,7 +10,7 @@ LL | let _: Option<()> = produces_string().and_then(takes_str); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a str) -> _` + found function signature `fn(&str) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -34,7 +34,7 @@ LL | let _: Option> = produces_string().map(takes_str); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a str) -> _` + found function signature `fn(&str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -58,7 +58,7 @@ LL | let _: Option> = produces_string().map(takes_str_mut); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a mut str) -> _` + found function signature `fn(&mut str) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure @@ -82,7 +82,7 @@ LL | let _ = produces_string().and_then(generic_ref); | required by a bound introduced by this call | = note: expected function signature `fn(String) -> _` - found function signature `for<'a> fn(&'a _) -> _` + found function signature `fn(&_) -> _` note: required by a bound in `Option::::and_then` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure diff --git a/tests/ui/nll/missing-universe-cause-issue-114907.rs b/tests/ui/nll/missing-universe-cause-issue-114907.rs index 9c69c6bdc367..a3eb5fceea99 100644 --- a/tests/ui/nll/missing-universe-cause-issue-114907.rs +++ b/tests/ui/nll/missing-universe-cause-issue-114907.rs @@ -31,8 +31,8 @@ fn accept(_: C) -> Handshake> { fn main() { let callback = |_| {}; accept(callback); - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR implementation of `FnOnce` is not general enough //~| ERROR higher-ranked subtype error diff --git a/tests/ui/nll/missing-universe-cause-issue-114907.stderr b/tests/ui/nll/missing-universe-cause-issue-114907.stderr index a616d29c4fea..26ad1efec055 100644 --- a/tests/ui/nll/missing-universe-cause-issue-114907.stderr +++ b/tests/ui/nll/missing-universe-cause-issue-114907.stderr @@ -1,26 +1,3 @@ -error[E0308]: mismatched types - --> $DIR/missing-universe-cause-issue-114907.rs:33:5 - | -LL | accept(callback); - | ^^^^^^^^^^^^^^^^ one type is more general than the other - | - = note: expected trait `for<'a> FnOnce(&'a ())` - found trait `FnOnce(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/missing-universe-cause-issue-114907.rs:32:20 - | -LL | let callback = |_| {}; - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/missing-universe-cause-issue-114907.rs:27:14 - | -LL | fn accept(_: C) -> Handshake> { - | ^^^^^^^^^^^ -help: consider specifying the type of the closure parameters - | -LL | let callback = |_: &_| {}; - | ~~~~~~~ - error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 | @@ -40,28 +17,25 @@ LL | accept(callback); = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0308]: mismatched types +error: implementation of `FnOnce` is not general enough --> $DIR/missing-universe-cause-issue-114907.rs:33:5 | LL | accept(callback); - | ^^^^^^^^^^^^^^^^ one type is more general than the other + | ^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: expected trait `for<'a> FnOnce(&'a ())` - found trait `FnOnce(&())` -note: this closure does not fulfill the lifetime requirements - --> $DIR/missing-universe-cause-issue-114907.rs:32:20 + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `FnOnce` is not general enough + --> $DIR/missing-universe-cause-issue-114907.rs:33:5 | -LL | let callback = |_| {}; - | ^^^ -note: the lifetime requirement is introduced here - --> $DIR/missing-universe-cause-issue-114907.rs:20:21 +LL | accept(callback); + | ^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | -LL | struct Handshake { - | ^^^^ -help: consider specifying the type of the closure parameters - | -LL | let callback = |_: &_| {}; - | ~~~~~~~ + = note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: higher-ranked subtype error --> $DIR/missing-universe-cause-issue-114907.rs:33:21 @@ -79,4 +53,3 @@ LL | accept(callback); error: aborting due to 6 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed index 8a67b20eec11..d95faef8ac44 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.fixed @@ -18,6 +18,16 @@ impl Index for A { } } +// This is used to make `use std::ops::Index;` not unused_import. +// details in fix(#122373) for issue #121331 +pub struct C; +impl Index for C { + type Output = (); + fn index(&self, _: str) -> &Self::Output { + &() + } +} + mod inner { pub trait Trait {} } @@ -29,4 +39,5 @@ use inner::Trait; impl Trait for () {} //~^ ERROR unnecessary qualification +impl Trait for A {} fn main() {} diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs index 528edb331cf5..0eee8f71ad4e 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.rs @@ -18,6 +18,16 @@ fn index(&self, _: str) -> &Self::Output { } } +// This is used to make `use std::ops::Index;` not unused_import. +// details in fix(#122373) for issue #121331 +pub struct C; +impl Index for C { + type Output = (); + fn index(&self, _: str) -> &Self::Output { + &() + } +} + mod inner { pub trait Trait {} } @@ -29,4 +39,5 @@ pub trait Trait {} impl inner::Trait for () {} //~^ ERROR unnecessary qualification +impl Trait for A {} fn main() {} diff --git a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr index bcda72107123..e105b754b719 100644 --- a/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr +++ b/tests/ui/resolve/issue-113808-invalid-unused-qualifications-suggestion.stderr @@ -16,7 +16,7 @@ LL + impl Index for A { | error: unnecessary qualification - --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:29:6 + --> $DIR/issue-113808-invalid-unused-qualifications-suggestion.rs:39:6 | LL | impl inner::Trait for () {} | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/resolve/unused-qualifications-suggestion.fixed b/tests/ui/resolve/unused-qualifications-suggestion.fixed index 6935f611b361..22e0daea4676 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.fixed +++ b/tests/ui/resolve/unused-qualifications-suggestion.fixed @@ -16,8 +16,10 @@ fn main() { use foo::bar; bar(); //~^ ERROR unnecessary qualification + bar(); use baz::qux::quux; quux(); //~^ ERROR unnecessary qualification + quux(); } diff --git a/tests/ui/resolve/unused-qualifications-suggestion.rs b/tests/ui/resolve/unused-qualifications-suggestion.rs index b3fe04ff0ea4..89516c1344a9 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.rs +++ b/tests/ui/resolve/unused-qualifications-suggestion.rs @@ -16,8 +16,10 @@ fn main() { use foo::bar; foo::bar(); //~^ ERROR unnecessary qualification + bar(); use baz::qux::quux; baz::qux::quux(); //~^ ERROR unnecessary qualification + quux(); } diff --git a/tests/ui/resolve/unused-qualifications-suggestion.stderr b/tests/ui/resolve/unused-qualifications-suggestion.stderr index e3dac37fc6e2..5b71ba9e2223 100644 --- a/tests/ui/resolve/unused-qualifications-suggestion.stderr +++ b/tests/ui/resolve/unused-qualifications-suggestion.stderr @@ -16,7 +16,7 @@ LL + bar(); | error: unnecessary qualification - --> $DIR/unused-qualifications-suggestion.rs:21:5 + --> $DIR/unused-qualifications-suggestion.rs:22:5 | LL | baz::qux::quux(); | ^^^^^^^^^^^^^^ diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs index c0e13a5f5f03..5d11941414f4 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.rs @@ -26,8 +26,8 @@ fn id(t: T) -> T { foo: &Foo { bools: &[false, true] }, bar: &Bar { bools: &[true, true] }, f: &id, - //~^ ERROR mismatched types - //~| ERROR mismatched types + //~^ ERROR implementation of `Fn` 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 }; diff --git a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr index 52c700c326e3..5c98a9d4fb4c 100644 --- a/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr +++ b/tests/ui/rfcs/rfc-1623-static/rfc1623-2.stderr @@ -1,21 +1,20 @@ -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/rfc1623-2.rs:28:8 | LL | f: &id, - | ^^^ one type is more general than the other + | ^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)` - found trait `Fn(&Foo<'_>)` + = note: `fn(&'2 Foo<'_>) -> &'2 Foo<'_> {id::<&'2 Foo<'_>>}` must implement `Fn<(&'1 Foo<'b>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 Foo<'_>,)>`, for some specific lifetime `'2` -error[E0308]: mismatched types +error: implementation of `Fn` is not general enough --> $DIR/rfc1623-2.rs:28:8 | LL | f: &id, - | ^^^ one type is more general than the other + | ^^^ implementation of `Fn` is not general enough | - = note: expected trait `for<'a, 'b> Fn(&'a Foo<'b>)` - found trait `Fn(&Foo<'_>)` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: `fn(&Foo<'2>) -> &Foo<'2> {id::<&Foo<'2>>}` must implement `Fn<(&'a Foo<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&Foo<'2>,)>`, for some specific lifetime `'2` error: implementation of `FnOnce` is not general enough --> $DIR/rfc1623-2.rs:28:8 @@ -37,4 +36,3 @@ LL | f: &id, error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/simd/const-err-trumps-simd-err.rs b/tests/ui/simd/const-err-trumps-simd-err.rs new file mode 100644 index 000000000000..06a747273ab7 --- /dev/null +++ b/tests/ui/simd/const-err-trumps-simd-err.rs @@ -0,0 +1,24 @@ +//@build-fail +//! Make sure that monomorphization-time const errors from `static_assert` take priority over the +//! error from simd_extract. Basically this checks that if a const fails to evaluate in some +//! function, we don't bother codegen'ing the function. +#![feature(generic_arg_infer)] +#![feature(core_intrinsics)] +#![feature(repr_simd)] +#![feature(inline_const)] +use std::intrinsics::simd::*; + +#[repr(simd)] +#[allow(non_camel_case_types)] +struct int8x4_t(u8,u8,u8,u8); + +fn get_elem(a: int8x4_t) -> u8 { + const { assert!(LANE < 4); } // the error should be here... + //~^ ERROR failed + //~| assertion failed + unsafe { simd_extract(a, LANE) } // ...not here +} + +fn main() { + get_elem::<4>(int8x4_t(0,0,0,0)); +} diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr new file mode 100644 index 000000000000..6e6aba8b6f18 --- /dev/null +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -0,0 +1,17 @@ +error[E0080]: evaluation of `get_elem::<4>::{constant#0}` failed + --> $DIR/const-err-trumps-simd-err.rs:16:13 + | +LL | const { assert!(LANE < 4); } // the error should be here... + | ^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: LANE < 4', $DIR/const-err-trumps-simd-err.rs:16:13 + | + = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info) + +note: the above error was encountered while instantiating `fn get_elem::<4>` + --> $DIR/const-err-trumps-simd-err.rs:23:5 + | +LL | get_elem::<4>(int8x4_t(0,0,0,0)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr index ee924522564a..f0acf1dbb963 100644 --- a/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr +++ b/tests/ui/suggestions/late-bound-in-borrow-closure-sugg.stderr @@ -10,7 +10,7 @@ LL | trader.set_closure(closure); | required by a bound introduced by this call | = note: expected closure signature `for<'a, 'b> fn(&'a mut Trader<'b>) -> _` - found closure signature `for<'a> fn(Trader<'a>) -> _` + found closure signature `fn(Trader<'_>) -> _` note: required by a bound in `Trader::<'a>::set_closure` --> $DIR/late-bound-in-borrow-closure-sugg.rs:15:50 | diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr new file mode 100644 index 000000000000..098ab71e946e --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.current.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + found existential trait ref `for<'a> Supertrait<'a, 'a>` + +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + found existential trait ref `for<'a> Supertrait<'a, 'a>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr new file mode 100644 index 000000000000..ac516fd69753 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.next.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ok.rs:17:5 + | +LL | fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { + | ------------------------------- expected `&dyn for<'a> Supertrait<'a, 'a>` because of return type +LL | x + | ^ expected trait `Supertrait`, found trait `Subtrait` + | + = note: expected reference `&dyn for<'a> Supertrait<'a, 'a>` + found reference `&dyn for<'a, 'b> Subtrait<'a, 'b>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs new file mode 100644 index 000000000000..007432031797 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ok.rs @@ -0,0 +1,19 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// We should be able to instantiate a binder during trait upcasting. +// This test could be `check-pass`, but we should make sure that we +// do so in both trait solvers. +#![feature(trait_upcasting)] +#![crate_type = "rlib"] +trait Supertrait<'a, 'b> {} + +trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} + +impl<'a> Supertrait<'a, 'a> for () {} +impl<'a> Subtrait<'a, 'a> for () {} +fn ok(x: &dyn for<'a, 'b> Subtrait<'a, 'b>) -> &dyn for<'a> Supertrait<'a, 'a> { + x //~ ERROR mismatched types + //[current]~^ ERROR mismatched types +} diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr new file mode 100644 index 000000000000..bac829832687 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.current.stderr @@ -0,0 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a> Supertrait<'a, 'a>` + found existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | x + | ^ one type is more general than the other + | + = note: expected existential trait ref `for<'a> Supertrait<'a, 'a>` + found existential trait ref `for<'a, 'b> Supertrait<'a, 'b>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr new file mode 100644 index 000000000000..b82f1eef42b5 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.next.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/higher-ranked-upcasting-ub.rs:22:5 + | +LL | fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { + | ----------------------------------- expected `&dyn for<'a, 'b> Supertrait<'a, 'b>` because of return type +LL | x + | ^ expected trait `Supertrait`, found trait `Subtrait` + | + = note: expected reference `&dyn for<'a, 'b> Supertrait<'a, 'b>` + found reference `&dyn for<'a> Subtrait<'a, 'a>` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs new file mode 100644 index 000000000000..2cf6fc75e777 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/higher-ranked-upcasting-ub.rs @@ -0,0 +1,37 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +// We previously wrongly instantiated binders during trait upcasting, +// allowing the super trait to be more generic than the sub trait. +// This was unsound. +#![feature(trait_upcasting)] +trait Supertrait<'a, 'b> { + fn cast(&self, x: &'a str) -> &'b str; +} + +trait Subtrait<'a, 'b>: Supertrait<'a, 'b> {} + +impl<'a> Supertrait<'a, 'a> for () { + fn cast(&self, x: &'a str) -> &'a str { + x + } +} +impl<'a> Subtrait<'a, 'a> for () {} +fn unsound(x: &dyn for<'a> Subtrait<'a, 'a>) -> &dyn for<'a, 'b> Supertrait<'a, 'b> { + x //~ ERROR mismatched types + //[current]~^ ERROR mismatched types +} + +fn transmute<'a, 'b>(x: &'a str) -> &'b str { + unsound(&()).cast(x) +} + +fn main() { + let x; + { + let mut temp = String::from("hello there"); + x = transmute(temp.as_str()); + } + println!("{x}"); +} diff --git a/tests/ui/typeck/mismatched-map-under-self.stderr b/tests/ui/typeck/mismatched-map-under-self.stderr index 13678b4b8272..322bf349f92f 100644 --- a/tests/ui/typeck/mismatched-map-under-self.stderr +++ b/tests/ui/typeck/mismatched-map-under-self.stderr @@ -27,7 +27,7 @@ LL | self.map(Insertable::values).unwrap_or_default() | required by a bound introduced by this call | = note: expected function signature `fn(T) -> _` - found function signature `for<'a> fn(&'a _) -> _` + found function signature `fn(&_) -> _` note: required by a bound in `Option::::map` --> $SRC_DIR/core/src/option.rs:LL:COL help: consider wrapping the function in a closure