diff --git a/compiler/rustc_attr_ir/src/lang_items.rs b/compiler/rustc_attr_ir/src/lang_items.rs index f2ad7abba755d..c1ad05dc8e4a8 100644 --- a/compiler/rustc_attr_ir/src/lang_items.rs +++ b/compiler/rustc_attr_ir/src/lang_items.rs @@ -465,6 +465,9 @@ language_item_table! { // Used to fallback `{float}` to `f32` when `f32: From<{float}>` From, sym::From, from_trait, Target::Trait, GenericRequirement::Exact(1); FromFn, sym::from, from_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; + + // Experimental lang item for `Reflection and comptime`(https://goals.rust-lang.org/2025h2/reflection-and-comptime.html) + FnPtr, sym::FnPtr, fn_ptr, Target::Struct, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 534cd1327bbe5..2f5793d5b3672 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -589,6 +589,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { // result in basically the exact same error being reported to // the user. Avoid that. let mut deduplicate_errors = FxIndexSet::default(); + let mut failed_type_tests = Vec::new(); for type_test in &self.type_tests { debug!("check_type_test: {:?}", type_test); @@ -609,8 +610,40 @@ impl<'tcx> RegionInferenceContext<'tcx> { continue; } - // Type-test failed. Report the error. + // Type-test failed. Collect it so we can suppress redundant errors below. let erased_generic_kind = infcx.tcx.erase_and_anonymize_regions(type_test.generic_kind); + failed_type_tests.push((erased_generic_kind, type_test)); + } + + // An async body can produce both `G: 'static` and `G: 'a` type-test failures at + // the same span, as in `tests/ui/async-await/spurious-static-bound-issue-115376.rs`. + // Reporting the weaker bound adds a redundant diagnostic and suggests a lifetime + // bound that cannot fix the missing `G: 'static` requirement. Keep the `'static` + // error and suppress weaker failures for the same erased generic kind and span. + // This is a diagnostic heuristic, using the same erasure as deduplication below. + // + // Collect all failed `'static` bounds before reporting errors so suppression does + // not depend on the order of the type tests. Compare SCCs because a lower-bound + // region can be equivalent to `'static` without being `fr_static` itself. + let static_scc = self.constraint_sccs.scc(self.universal_regions().fr_static); + let static_bound_errors: FxIndexSet<_> = failed_type_tests + .iter() + .filter_map(|&(erased_generic_kind, type_test)| { + if self.constraint_sccs.scc(type_test.lower_bound) == static_scc { + Some((erased_generic_kind, type_test.span)) + } else { + None + } + }) + .collect(); + + // If `G: 'static` failed at this span, then same-span `G: 'a` failures are weaker. + for (erased_generic_kind, type_test) in failed_type_tests { + if self.constraint_sccs.scc(type_test.lower_bound) != static_scc + && static_bound_errors.contains(&(erased_generic_kind, type_test.span)) + { + continue; + } // Skip duplicate-ish errors. if deduplicate_errors.insert(( diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index d6c25cf524a5c..e06549090226e 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -284,6 +284,7 @@ impl ExtraBackendMethods for AotDriver { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + _bitcode_needed: bool, ) -> (ModuleCodegen, u64) { let start_time = Instant::now(); diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index cbc7db8e9e23f..2fb5459a20283 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -365,6 +365,7 @@ impl ExtraBackendMethods for GccCodegenBackend { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + _bitcode_needed: bool, ) -> (ModuleCodegen, u64) { base::compile_codegen_unit( tcx, diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 3d2362be02f27..c22318c0ec8a7 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -10,6 +10,8 @@ use rustc_middle::mir::interpret::{PointerArithmetic, Scalar as ConstScalar}; use rustc_middle::ty::Instance; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::{bug, span_bug}; +use rustc_session::Session; +use rustc_session::config::Lto; use rustc_span::{Pos, Span, Symbol, sym}; use rustc_target::asm::*; use rustc_target::spec::HasTargetSpec; @@ -594,30 +596,45 @@ pub(crate) fn inline_asm_call<'ll>( let key = "srcloc"; let kind = bx.get_md_kind_id(key); - // `srcloc` contains one 64-bit integer for each line of assembly code, - // where the lower 32 bits hold the lo byte position and the upper 32 bits - // hold the hi byte position. - let mut srcloc = vec![]; - if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { - // LLVM inserts an extra line to add the ".intel_syntax", so add - // a dummy srcloc entry for it. - // - // Don't do this if we only have 1 line span since that may be - // due to the asm template string coming from a macro. LLVM will - // default to the first srcloc for lines that don't have an - // associated srcloc. - srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); + if allow_raw_span_inline_asm_srcloc(bx.tcx.sess, bx.bitcode_needed) { + // `srcloc` contains one 64-bit integer for each line of assembly code, + // where the lower 32 bits hold the lo byte position and the upper 32 bits + // hold the hi byte position. + let mut srcloc = vec![]; + if dia == llvm::AsmDialect::Intel && line_spans.len() > 1 { + // LLVM inserts an extra line to add the ".intel_syntax", so add + // a dummy srcloc entry for it. + // + // Don't do this if we only have 1 line span since that may be + // due to the asm template string coming from a macro. LLVM will + // default to the first srcloc for lines that don't have an + // associated srcloc. + srcloc.push(llvm::LLVMValueAsMetadata(bx.const_u64(0))); + } + srcloc.extend(line_spans.iter().map(|span| { + llvm::LLVMValueAsMetadata( + bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), + ) + })); + bx.cx.set_metadata_node(call, kind, &srcloc); } - srcloc.extend(line_spans.iter().map(|span| { - llvm::LLVMValueAsMetadata( - bx.const_u64(u64::from(span.lo().to_u32()) | (u64::from(span.hi().to_u32()) << 32)), - ) - })); - bx.cx.set_metadata_node(call, kind, &srcloc); Some(call) } +/// Whenever inline assembly bitcode is built, its `srcloc` contains the raw span numbers +/// as location cookies. This is problematic since that is nondeterministic when using +/// the parallel frontend. Even without parallelism, the cookies are meaningless in another +/// rustc session. +/// +/// Discussion about replacing the cookies with something stable: rust-lang/rust#150451 +fn allow_raw_span_inline_asm_srcloc(sess: &Session, bitcode_needed: bool) -> bool { + // even for Lto::ThinLocal, where the bitcode isn't serialized into files, the changes in + // raw span positions would reflect in the LTO module hashes, which could lead to + // nondeterminism + sess.lto() == Lto::No && !bitcode_needed +} + /// If the register is an xmm/ymm/zmm register then return its index. fn xmm_reg_index(reg: InlineAsmReg) -> Option { use X86InlineAsmReg::*; diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index bbe968e8248ad..5316b7425be47 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -64,6 +64,7 @@ pub(crate) fn iter_global_aliases(llmod: &llvm::Module) -> ValueIter<'_> { pub(crate) fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64) { let start_time = Instant::now(); @@ -71,7 +72,7 @@ pub(crate) fn compile_codegen_unit( let (module, _) = tcx.dep_graph.with_task( dep_node, tcx, - || module_codegen(tcx, cgu_name), + || module_codegen(tcx, cgu_name, bitcode_needed), Some(dep_graph::hash_result), ); let time_to_codegen = start_time.elapsed(); @@ -80,7 +81,11 @@ pub(crate) fn compile_codegen_unit( // the time we needed for codegenning it. let cost = time_to_codegen.as_nanos() as u64; - fn module_codegen(tcx: TyCtxt<'_>, cgu_name: Symbol) -> ModuleCodegen { + fn module_codegen( + tcx: TyCtxt<'_>, + cgu_name: Symbol, + needs_bitcode: bool, + ) -> ModuleCodegen { let cgu = tcx.codegen_unit(cgu_name); let _prof_timer = tcx.prof.generic_activity_with_arg_recorder("codegen_module", |recorder| { @@ -90,7 +95,7 @@ pub(crate) fn compile_codegen_unit( // Instantiate monomorphizations without filling out definitions yet... let llvm_module = ModuleLlvm::new(tcx, cgu_name.as_str()); { - let mut cx = CodegenCx::new(tcx, cgu, &llvm_module); + let mut cx = CodegenCx::new(tcx, cgu, &llvm_module, needs_bitcode); // Declare and store globals shared by all offload kernels // diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index c737fad66e573..08a74774305c6 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -93,6 +93,7 @@ pub(crate) type CodegenCx<'ll, 'tcx> = GenericCx<'ll, FullCx<'ll, 'tcx>>; pub(crate) struct FullCx<'ll, 'tcx> { pub tcx: TyCtxt<'tcx>, + pub bitcode_needed: bool, pub scx: SimpleCx<'ll>, pub use_dll_storage_attrs: bool, pub tls_model: llvm::ThreadLocalMode, @@ -608,6 +609,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { tcx: TyCtxt<'tcx>, codegen_unit: &'tcx CodegenUnit<'tcx>, llvm_module: &'ll crate::ModuleLlvm, + bitcode_needed: bool, ) -> Self { // An interesting part of Windows which MSVC forces our hand on (and // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` @@ -703,6 +705,7 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> { GenericCx( FullCx { tcx, + bitcode_needed, scx: SimpleCx::new(llmod, llcx, tcx.data_layout.pointer_size()), use_dll_storage_attrs, tls_model, diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 775e1dcf2ffea..ca16d33b90256 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -112,8 +112,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64) { - base::compile_codegen_unit(tcx, cgu_name) + base::compile_codegen_unit(tcx, cgu_name, bitcode_needed) } } diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 78cdd3e38f68c..e8b25eb4359d0 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -15,7 +15,6 @@ use rustc_errors::{ Level, MultiSpan, Style, Suggestions, catch_fatal_errors, }; use rustc_fs_util::link_or_copy; -use rustc_hir::find_attr; use rustc_incremental::{copy_cgu_workproduct_to_incr_comp_cache_dir, in_incr_comp_dir_sess}; use rustc_macros::{Decodable, Encodable}; use rustc_metadata::fs::copy_to_stdout; @@ -114,7 +113,7 @@ pub struct ModuleConfig { } impl ModuleConfig { - fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { + pub(crate) fn new(kind: ModuleKind, tcx: TyCtxt<'_>, no_builtins: bool) -> ModuleConfig { // If it's a regular module, use `$regular`, otherwise use `$other`. // `$regular` and `$other` are evaluated lazily. macro_rules! if_regular { @@ -426,15 +425,12 @@ fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool { pub(crate) fn start_async_codegen( backend: B, tcx: TyCtxt<'_>, + regular_config: Arc, + allocator_config: Arc, allocator_module: Option>, ) -> OngoingCodegen { let (coordinator_send, coordinator_receive) = channel(); - let no_builtins = find_attr!(tcx, crate, NoBuiltins); - - let regular_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); - let allocator_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (codegen_worker_send, codegen_worker_receive) = channel(); @@ -444,8 +440,8 @@ pub(crate) fn start_async_codegen( shared_emitter, codegen_worker_send, coordinator_receive, - Arc::new(regular_config), - Arc::new(allocator_config), + regular_config, + allocator_config, allocator_module, coordinator_send.clone(), ); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index c870d1694d068..8dd129f45cc5a 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -42,7 +42,7 @@ use tracing::{debug, info}; use crate::assert_module_sources::CguReuse; use crate::back::link::are_upstream_rust_objects_already_included; use crate::back::write::{ - ComputedLtoType, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, + ComputedLtoType, ModuleConfig, OngoingCodegen, compute_per_cgu_lto_type, start_async_codegen, submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, }; use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; @@ -52,7 +52,7 @@ use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo, - ModuleCodegen, diagnostics, meth, mir, + ModuleCodegen, ModuleKind, diagnostics, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -762,7 +762,18 @@ pub fn codegen_crate< None }; - let ongoing_codegen = start_async_codegen(backend.clone(), tcx, allocator_module); + let no_builtins = find_attr!(tcx, crate, NoBuiltins); + let regular_module_config = ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins); + let bitcode_needed = regular_module_config.bitcode_needed(); + let allocator_module_config = ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins); + + let ongoing_codegen = start_async_codegen( + backend.clone(), + tcx, + Arc::new(regular_module_config), + Arc::new(allocator_module_config), + allocator_module, + ); // For better throughput during parallel processing by LLVM, we used to sort // CGUs largest to smallest. This would lead to better thread utilization @@ -822,7 +833,8 @@ pub fn codegen_crate< let start_time = Instant::now(); let pre_compiled_cgus = par_map(cgus, |(i, _)| { - let module = backend.compile_codegen_unit(tcx, codegen_units[i].name()); + let module = + backend.compile_codegen_unit(tcx, codegen_units[i].name(), bitcode_needed); (i, IntoDynSyncSend(module)) }); @@ -846,7 +858,7 @@ pub fn codegen_crate< cgu.0 } else { let start_time = Instant::now(); - let module = backend.compile_codegen_unit(tcx, cgu.name()); + let module = backend.compile_codegen_unit(tcx, cgu.name(), bitcode_needed); total_codegen_time += start_time.elapsed(); module }; diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 11878c1f5165d..2435cca50a0f3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -177,5 +177,6 @@ pub trait ExtraBackendMethods: Send + Sync + DynSend + DynSync { &self, tcx: TyCtxt<'_>, cgu_name: Symbol, + bitcode_needed: bool, ) -> (ModuleCodegen, u64); } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 7c10dd04f39f3..ce4c8497463c8 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -612,6 +612,15 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ecx.write_scalar(Scalar::from_bool(ty.is_signed()), dest)?; } + sym::type_id_points_mutably => { + let ty = ecx.read_type_id(&args[0])?; + let is_mutable = matches!( + ty.kind(), + ty::RawPtr(_, Mutability::Mut) | &ty::Ref(_, _, Mutability::Mut) + ); + ecx.write_scalar(Scalar::from_bool(is_mutable), dest)?; + } + sym::size_of_type_id => { let ty = ecx.read_type_id(&args[0])?; let layout = ecx.layout_of(ty)?; @@ -691,6 +700,33 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { ); ecx.write_type_id(frt, dest)?; } + sym::type_id_function_ptr => { + let ty = ecx.read_type_id(&args[0])?; + let variant_index = if let ty::FnPtr(sig, fn_header) = ty.kind() { + let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?; + let field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; + let sig = sig.skip_binder(); // FIXME: handle lifetime bounds + ecx.write_fn_ptr_type_info(field_place, &sig, fn_header)?; + variant + } else { + ecx.project_downcast_named(dest, sym::None)?.0 + }; + ecx.write_discriminant(variant_index, dest)?; + } + sym::type_id_points_to => { + let ty = ecx.read_type_id(&args[0])?; + let variant_index = if let ty::RawPtr(pointee_ty, _) | ty::Ref(_, pointee_ty, _) = + ty.kind() + { + let (variant, variant_place) = ecx.project_downcast_named(dest, sym::Some)?; + let field_place = ecx.project_field(&variant_place, FieldIdx::ZERO)?; + ecx.write_type_id(*pointee_ty, &field_place)?; + variant + } else { + ecx.project_downcast_named(dest, sym::None)?.0 + }; + ecx.write_discriminant(variant_index, dest)?; + } sym::type_id_variants => { let ty = ecx.read_type_id(&args[0])?; diff --git a/compiler/rustc_const_eval/src/const_eval/type_info.rs b/compiler/rustc_const_eval/src/const_eval/type_info.rs index f6e0208d98835..8d93aee57a518 100644 --- a/compiler/rustc_const_eval/src/const_eval/type_info.rs +++ b/compiler/rustc_const_eval/src/const_eval/type_info.rs @@ -3,7 +3,6 @@ mod adt; use std::borrow::Cow; use rustc_abi::{ExternAbi, FieldIdx}; -use rustc_ast::Mutability; use rustc_hir::attrs::lang_items::LangItem; use rustc_middle::span_bug; use rustc_middle::ty::layout::TyAndLayout; @@ -134,23 +133,14 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { self.project_downcast_named(&field_dest, sym::Str)?; variant } - ty::Ref(_, ty, mutability) => { - let (variant, variant_place) = + ty::Ref(_, _, _) => { + let (variant, _) = self.project_downcast_named(&field_dest, sym::Reference)?; - let reference_place = - self.project_field(&variant_place, FieldIdx::ZERO)?; - self.write_reference_type_info(reference_place, *ty, *mutability)?; - variant } - ty::RawPtr(ty, mutability) => { - let (variant, variant_place) = + ty::RawPtr(_, _) => { + let (variant, _variant_place) = self.project_downcast_named(&field_dest, sym::Pointer)?; - let pointer_place = - self.project_field(&variant_place, FieldIdx::ZERO)?; - - self.write_pointer_type_info(pointer_place, *ty, *mutability)?; - variant } ty::Dynamic(predicates, region) => { @@ -160,16 +150,9 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { self.write_dyn_trait_type_info(dyn_place, *predicates, *region)?; variant } - ty::FnPtr(sig, fn_header) => { - let (variant, variant_place) = + ty::FnPtr(_, _) => { + let (variant, _) = self.project_downcast_named(&field_dest, sym::FnPtr)?; - let fn_ptr_place = - self.project_field(&variant_place, FieldIdx::ZERO)?; - - // FIXME: handle lifetime bounds - let sig = sig.skip_binder(); - - self.write_fn_ptr_type_info(fn_ptr_place, &sig, fn_header)?; variant } ty::Foreign(_) @@ -301,31 +284,6 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } - pub(crate) fn write_reference_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - ty: Ty<'tcx>, - mutability: Mutability, - ) -> InterpResult<'tcx> { - // Iterate over all fields of `type_info::Reference`. - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - - match field.name { - // Write the `TypeId` of the reference's inner type to the `ty` field. - sym::pointee => self.write_type_id(ty, &field_place)?, - // Write the boolean representing the reference's mutability to the `mutable` field. - sym::mutable => { - self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)? - } - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - interp_ok(()) - } - pub(crate) fn write_type_id_generics( &mut self, place: &impl Writeable<'tcx, CtfeProvenance>, @@ -383,7 +341,7 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { let field_place = self.project_field(&place, field_idx)?; match field.name { - sym::unsafety => { + sym::is_unsafe => { self.write_scalar(Scalar::from_bool(!fn_sig_kind.is_safe()), &field_place)?; } sym::abi => match fn_sig_kind.abi() { @@ -444,30 +402,4 @@ impl<'tcx> InterpCx<'tcx, CompileTimeMachine<'tcx>> { interp_ok(()) } - - pub(crate) fn write_pointer_type_info( - &mut self, - place: impl Writeable<'tcx, CtfeProvenance>, - ty: Ty<'tcx>, - mutability: Mutability, - ) -> InterpResult<'tcx> { - // Iterate over all fields of `type_info::Pointer`. - for (field_idx, field) in - place.layout().ty.ty_adt_def().unwrap().non_enum_variant().fields.iter_enumerated() - { - let field_place = self.project_field(&place, field_idx)?; - - match field.name { - // Write the `TypeId` of the pointer's inner type to the `ty` field. - sym::pointee => self.write_type_id(ty, &field_place)?, - // Write the boolean representing the pointer's mutability to the `mutable` field. - sym::mutable => { - self.write_scalar(Scalar::from_bool(mutability.is_mut()), &field_place)? - } - other => span_bug!(self.tcx.def_span(field.did), "unimplemented field {other}"), - } - } - - interp_ok(()) - } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 30d7127ccd8fa..61f72ee4b5de1 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -202,8 +202,11 @@ fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -> hi | sym::type_id_eq | sym::type_id_field_representing_type | sym::type_id_fields + | sym::type_id_function_ptr | sym::type_id_generics | sym::type_id_is_signed + | sym::type_id_points_mutably + | sym::type_id_points_to | sym::type_id_variants | sym::type_id_vtable | sym::type_name @@ -317,7 +320,20 @@ pub(crate) fn check_intrinsic_type( (0, 0, vec![type_id_ty(), tcx.types.usize, tcx.types.usize], type_id_ty()) } sym::type_id_fields => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.usize), + sym::type_id_function_ptr => { + let fn_ptr = tcx.require_lang_item(LangItem::FnPtr, span); + let fn_ptr_adt_ref = tcx.adt_def(fn_ptr); + let fn_ptr_ty = Ty::new_adt(tcx, fn_ptr_adt_ref, ty::List::empty()); + + let option = tcx.require_lang_item(LangItem::Option, span); + let option_adt_ref = tcx.adt_def(option); + let option_args = tcx.mk_args(&[fn_ptr_ty.into()]); + let option_fn_ptr_ty = Ty::new_adt(tcx, option_adt_ref, option_args); + (0, 0, vec![type_id_ty()], option_fn_ptr_ty) + } sym::type_id_is_signed => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_points_mutably => (0, 0, vec![type_id_ty()], tcx.types.bool), + sym::type_id_points_to => (0, 0, vec![type_id_ty()], Ty::new_option(tcx, type_id_ty())), sym::type_id_variants => (0, 0, vec![type_id_ty()], tcx.types.usize), sym::variant_name => (0, 0, vec![type_id_ty(), tcx.types.usize], Ty::new_static_str(tcx)), sym::variant_non_exhaustive => (0, 0, vec![type_id_ty(), tcx.types.usize], tcx.types.bool), diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 13fbb3a2c0c3c..db9dbc1c3f482 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -55,14 +55,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ExprKind::Block { block: ast_block } => { this.ast_block(destination, block, ast_block, source_info) } - ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr( - destination, - block, - scrutinee, - arms, - expr_span, - this.thir[scrutinee].span, - ), + ExprKind::Match { scrutinee, ref arms, .. } => { + this.match_expr(destination, block, scrutinee, arms, expr_span) + } ExprKind::If { cond, then, else_opt, if_then_scope } => { let then_span = this.thir[then].span; let then_source_info = this.source_info(then_span); @@ -303,9 +298,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Logic for `match`. let scrutinee_span = this.thir.exprs[scrutinee].span; - let scrutinee_place_builder = unpack!( - body_block = this.lower_scrutinee(body_block, scrutinee, scrutinee_span) - ); + let scrutinee_place_builder = + unpack!(body_block = this.lower_scrutinee(body_block, scrutinee)); let match_start_span = match_span.shrink_to_lo().to(scrutinee_span); diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index a520acda5e6c8..01505fb9ec8ae 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -339,10 +339,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_id: ExprId, arms: &[ArmId], span: Span, - scrutinee_span: Span, ) -> BlockAnd<()> { - let scrutinee_place = - unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span)); + let scrutinee_span = self.thir[scrutinee_id].span; + let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee_id)); let match_start_span = span.shrink_to_lo().to(scrutinee_span); let patterns = arms @@ -378,11 +377,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, mut block: BasicBlock, scrutinee_id: ExprId, - scrutinee_span: Span, ) -> BlockAnd> { let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee_id)); if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) { - let source_info = self.source_info(scrutinee_span); + let source_info = self.source_info(self.thir[scrutinee_id].span); self.cfg.push_place_mention(block, source_info, scrutinee_place); } @@ -612,9 +610,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } _ => { - let initializer = &self.thir[initializer_id]; - let place_builder = - unpack!(block = self.lower_scrutinee(block, initializer_id, initializer.span)); + let place_builder = unpack!(block = self.lower_scrutinee(block, initializer_id)); self.place_into_pattern(block, irrefutable_pat, place_builder, true) } } @@ -2334,7 +2330,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { declare_let_bindings: DeclareLetBindings, ) -> BlockAnd<()> { let expr_span = self.thir[expr_id].span; - let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span)); + let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id)); let built_tree = self.lower_match_tree( block, expr_span, diff --git a/compiler/rustc_mir_transform/src/coverage/query.rs b/compiler/rustc_mir_transform/src/coverage/query.rs index 6ffb85d7b90a8..a4d39f09b724d 100644 --- a/compiler/rustc_mir_transform/src/coverage/query.rs +++ b/compiler/rustc_mir_transform/src/coverage/query.rs @@ -1,5 +1,6 @@ use rustc_hir::attrs::CoverageAttrKind; -use rustc_hir::find_attr; +use rustc_hir::def::DefKind; +use rustc_hir::{self as hir, find_attr}; use rustc_index::bit_set::DenseBitSet; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::{ @@ -30,11 +31,23 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { // 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() { + let def_kind = tcx.def_kind(def_id); + if !def_kind.is_fn_like() { trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)"); return false; } + // Comptime functions can't exist at runtime, so instrumenting them is useless. + // This also avoids an ICE when getting the symbol name for an unused-function record + // (due to ). + // We check `def_kind` first to avoid any unexpected panics from merely asking for constness. + if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) + && matches!(tcx.constness(def_id), hir::Constness::Const { always: true }) + { + trace!("InstrumentCoverage skipped for {def_id:?} (comptime)"); + return false; + } + if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NAKED) { trace!("InstrumentCoverage skipped for {def_id:?} (`#[naked]`)"); return false; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 7665df4a4e5ae..68d28f9227dbe 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1153,6 +1153,7 @@ symbols! { is, is_auto, is_splatted, + is_unsafe, is_val_statically_known, isa_attribute, isize, @@ -2170,8 +2171,11 @@ symbols! { type_id_eq, type_id_field_representing_type, type_id_fields, + type_id_function_ptr, type_id_generics, type_id_is_signed, + type_id_points_mutably, + type_id_points_to, type_id_variants, type_id_vtable, type_info, @@ -2263,7 +2267,6 @@ symbols! { unsafe_no_drop_flag, unsafe_pinned, unsafe_unpin, - unsafety, unsize, unsized_const_param_ty, unsized_const_params, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index d1a24e0054115..88d2184ed95b2 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -577,10 +577,12 @@ pub enum BuiltinImplSource { /// unless more specific information is necessary. Misc, /// A built-in impl for trait objects. The index is only used in winnowing. + // FIXME(-Znext-solver=no): The new solver does not need this index, remove! Object(usize), /// A built-in implementation of `Upcast` for trait objects to other trait objects. /// /// The index is only used for winnowing. + // FIXME(-Znext-solver=no): The new solver does not need this index, remove! TraitUpcasting(usize), } diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a99633456de0b..f8cc81228b27b 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3101,6 +3101,15 @@ pub fn field_representing_type_name(_frt_type_id: crate::any::TypeId) -> &'stati #[rustc_comptime] pub fn field_representing_type_offset(_frt_type_id: crate::any::TypeId) -> usize; +/// Given a `TypeId` that represents a function pointer returns an [`core::mem::type_info::FnPtr`]. +/// When called on something else this returns `None`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::function_ptr`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_function_ptr(_type_id: crate::any::TypeId) -> Option; + /// Checks whether this type is non-exhaustive. #[rustc_intrinsic] #[unstable(feature = "core_intrinsics", issue = "none")] @@ -3114,6 +3123,26 @@ pub fn non_exhaustive(_id: crate::any::TypeId) -> bool; #[rustc_comptime] pub fn type_id_generics(_id: crate::any::TypeId) -> &'static [crate::mem::type_info::Generic]; +// FIXME(reflection): Pick a consistent naming scheme for the intrinsics. Right now we got +// type_id_, _type_id and intrinsics not mentioning type_id at all. +/// Given a `TypeId` that represents a pointer this returns the `TypeId` which that pointer +/// points to. When called on anything else this returns None. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_to`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_to(_id: crate::any::TypeId) -> Option; + +/// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. +/// When called on anything else this returns `false`. +/// +/// The more user-friendly version of this intrinsic is [`core::any::TypeId::points_mutably`]. +#[rustc_intrinsic] +#[unstable(feature = "core_intrinsics", issue = "none")] +#[rustc_comptime] +pub fn type_id_points_mutably(_id: crate::any::TypeId) -> bool; + /// Lowers in MIR to `Rvalue::Aggregate` with `AggregateKind::RawPtr`. /// /// This is used to implement functions like `slice::from_raw_parts_mut` and diff --git a/library/core/src/mem/type_info.rs b/library/core/src/mem/type_info.rs index 111664775ca8d..1f38339a7421b 100644 --- a/library/core/src/mem/type_info.rs +++ b/library/core/src/mem/type_info.rs @@ -100,12 +100,14 @@ pub enum TypeKind { /// String slice type. Str(Str), /// References. - Reference(Reference), + Reference, /// Pointers. - Pointer(Pointer), + Pointer, /// Function pointers. - FnPtr(FnPtr), + FnPtr, /// FIXME(#146922): add all the common types + /// non exhaustive list: + /// - Never Other, } @@ -207,65 +209,55 @@ pub struct Str { // No additional information to provide for now. } -/// Compile-time type information about references. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Reference { - /// The type of the value being referred to. - pub pointee: TypeId, - /// Whether this reference is mutable or not. - pub mutable: bool, -} - -/// Compile-time type information about pointers. -#[derive(Debug)] -#[non_exhaustive] -#[unstable(feature = "type_info", issue = "146922")] -pub struct Pointer { - /// The type of the value being pointed to. - pub pointee: TypeId, - /// Whether this pointer is mutable or not. - pub mutable: bool, -} - #[derive(Debug)] +#[lang = "FnPtr"] #[unstable(feature = "type_info", issue = "146922")] /// Function pointer, e.g. fn(u8), pub struct FnPtr { - /// Unsafety, true is unsafe - pub unsafety: bool, - - /// Abi, e.g. extern "C" - pub abi: Abi, - - /// Function inputs - pub inputs: &'static [TypeId], - - /// Function return type, default is TypeId::of::<()> - pub output: TypeId, - - /// Vardiadic function, e.g. extern "C" fn add(n: usize, mut args: ...); - pub variadic: bool, - + is_unsafe: bool, + abi: Abi, + inputs: &'static [TypeId], + output: TypeId, + variadic: bool, // FIXME(splat): should these fields be private, or merged into an Option? /// Is any function argument splatted? - pub is_splatted: bool, + is_splatted: bool, - /// The index of the splatted function argument in `inputs`, only valid if `is_splatted` is true. - /// e.g. in `fn overload(a: u8, #[rustc_splat] b: (f32, usize))` the index is 1, and it can be called - /// as `overload(a, 1.0, 2)`. - pub splatted_index: u8, + splatted_index: u8, } impl FnPtr { /// Returns the splatted function argument index, or `None` if no argument is splatted. + /// + /// e.g. in `fn overload(a: u8, #[rustc_splat] b: (f32, usize))` the index is 1, + /// and it can be called as `overload(a, 1.0, 2)`. pub const fn splatted(&self) -> Option { if self.is_splatted { Some(self.splatted_index) } else { None } } + /// Whether this function is variadic, e.g. extern "C" fn add(n: usize, mut args: ...); + pub const fn is_variadic(&self) -> bool { + self.variadic + } + /// whether this refers to an unsafe function. + pub const fn is_unsafe(&self) -> bool { + self.is_unsafe + } + /// Returns the application binary interface. For example extern "C". + pub const fn abi(&self) -> Abi { + self.abi + } + /// The types of the functions parameters + pub const fn inputs(&self) -> &'static [TypeId] { + self.inputs + } + /// List of the types returned by the function. For a function with no output + /// specified this returns `TypeId::of<()>`. + pub const fn output(&self) -> TypeId { + self.output + } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] #[non_exhaustive] #[unstable(feature = "type_info", issue = "146922")] /// Abi of [FnPtr] @@ -567,6 +559,86 @@ impl TypeId { pub fn generics(self) -> &'static [Generic] { intrinsics::type_id_generics(self) } + + /// Given a `TypeId` that represents a pointer this returns the `TypeId` + /// which that pointer points to. When called on anything else this returns + /// None. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert_eq!( + /// const { TypeId::of::<&i32>().points_to() }, + /// const { Some(TypeId::of::()) }, + /// ); + /// + /// assert_eq!( + /// const { TypeId::of::<*const i32>().points_to() }, + /// const { Some(TypeId::of::()) }, + /// ); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn points_to(self) -> Option { + intrinsics::type_id_points_to(self) + } + + /// Given a `TypeId` that represents a pointer returns whether that pointer is mutable. + /// When called on anything else this returns `false`. + /// + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// + /// assert!(const { TypeId::of::<&mut i32>().points_mutably() }); + /// assert!(const { !TypeId::of::<&i32>().points_mutably() }); + /// + /// assert!(!const { TypeId::of::<*const i32>().points_mutably() }); + /// assert!(const { TypeId::of::<*mut i32>().points_mutably() }); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn points_mutably(self) -> bool { + intrinsics::type_id_points_mutably(self) + } + + /// Given a `TypeId` that represents a function pointer returns an + /// [`FnPtr`]. When called on something else this returns `None`. + /// ``` + /// #![feature(type_info)] + /// use std::any::TypeId; + /// use std::mem::type_info::{Abi, FnPtr}; + /// + /// const F: FnPtr = TypeId::of:: usize>() + /// .function_ptr() + /// .expect("TypeId of a function ptr"); + /// + /// assert!(F.inputs() == [TypeId::of::(), TypeId::of::()]); + /// assert!(F.output() == TypeId::of::()); + /// assert!(F.abi() == Abi::default()); + /// ``` + /// ``` + /// #![feature(type_info)] + /// # use std::any::TypeId; + /// # use std::mem::type_info::{Abi, FnPtr}; + /// # + /// const F: FnPtr = TypeId::of::() + /// .function_ptr() + /// .expect("TypeId of a function ptr"); + /// + /// assert!(F.inputs() == []); + /// assert!(F.output() == TypeId::of::<()>()); + /// assert!(F.abi() == Abi::default()); + /// ``` + #[unstable(feature = "type_info", issue = "146922")] + #[rustc_const_unstable(feature = "type_info", issue = "146922")] + #[rustc_comptime] + pub fn function_ptr(self) -> Option { + intrinsics::type_id_function_ptr(self) + } } /// Variant representing type ID. Representing a variant of an enum. diff --git a/library/coretests/tests/mem/fn_ptr.rs b/library/coretests/tests/mem/fn_ptr.rs index 192054bcaf66b..862048e4090d4 100644 --- a/library/coretests/tests/mem/fn_ptr.rs +++ b/library/coretests/tests/mem/fn_ptr.rs @@ -3,233 +3,101 @@ use std::mem::type_info::{Abi, FnPtr, Type, TypeKind}; const STRING_TY: TypeId = const { TypeId::of::() }; const U8_TY: TypeId = const { TypeId::of::() }; -const _U8_REF_TY: TypeId = const { TypeId::of::<&u8>() }; const UNIT_TY: TypeId = const { TypeId::of::<()>() }; const TUPLE_STRING_U8_TY: TypeId = const { TypeId::of::<(String, u8)>() }; #[test] fn test_fn_ptrs() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + let f = const { TypeId::of::().function_ptr().unwrap() }; + assert_eq!(f.is_unsafe(), false); + assert_eq!(f.abi(), Abi::ExternRust); + assert_eq!(f.inputs(), &[]); + assert_eq!(f.output(), UNIT_TY); + assert_eq!(f.is_variadic(), false); + assert_eq!(f.splatted(), None); } + +#[test] +fn test_typekind() { + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!(const { Type::of::().kind }, TypeKind::FnPtr)); + assert!(matches!( + const { Type::of::().kind }, + TypeKind::FnPtr + )); +} + #[test] fn test_ref() { - const { - // references are tricky because the lifetimes give the references different type ids - // so we check the pointees instead - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - if output != UNIT_TY { - panic!(); - } - let TypeKind::Reference(reference) = ty1.info().kind else { - panic!(); - }; - if reference.pointee != U8_TY { - panic!(); - } - let TypeKind::Reference(reference) = ty2.info().kind else { - panic!(); - }; - if reference.pointee != U8_TY { - panic!(); - } - } + // references are tricky because the lifetimes give the references different type ids + // so we check the pointees instead + const F: FnPtr = TypeId::of::().function_ptr().unwrap(); + assert_eq!(const { F.inputs()[0].points_to() }, Some(U8_TY)); + assert_eq!(const { F.inputs()[1].points_to() }, Some(U8_TY)); } #[test] fn test_unsafe() { - let TypeKind::FnPtr(FnPtr { - unsafety: true, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!(const { TypeId::of::().function_ptr() }.unwrap().is_unsafe(), true); } + #[test] fn test_abi() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::ExternRust + ); - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternC, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::ExternC + ); - let TypeKind::FnPtr(FnPtr { - unsafety: true, - abi: Abi::Named("system"), - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().abi(), + Abi::Named("system") + ); } #[test] fn test_inputs() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); - assert_eq!(ty1, STRING_TY); - assert_eq!(ty2, U8_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().inputs(), + [STRING_TY, U8_TY] + ); - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[ty1, ty2], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, UNIT_TY); - assert_eq!(ty1, STRING_TY); - assert_eq!(ty2, U8_TY); + assert_eq!( + const { TypeId::of::().function_ptr() }.unwrap().inputs(), + [STRING_TY, U8_TY] + ); } #[test] fn test_output() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: &[], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - }) = (const { Type::of:: u8>().kind }) - else { - panic!(); - }; - assert_eq!(output, U8_TY); + let f = const { TypeId::of:: u8>().function_ptr() }.unwrap(); + assert_eq!(f.output(), U8_TY); } #[test] fn test_variadic() { - let TypeKind::FnPtr(FnPtr { - unsafety: false, - abi: Abi::ExternC, - inputs: [ty1], - output, - variadic: true, - is_splatted: false, - splatted_index: _, - }) = &(const { Type::of::().kind }) - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, U8_TY); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.abi(), Abi::ExternC); + assert_eq!(f.inputs(), [U8_TY]); + assert_eq!(f.is_variadic(), true); } #[test] fn test_splat() { - #[rustfmt::skip] - let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { - panic!(); - }; - let FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: [ty1], - output, - variadic: false, - is_splatted: true, - splatted_index: 0, - } = fn_ptr_ty - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, TUPLE_STRING_U8_TY); - assert_eq!(fn_ptr_ty.splatted(), Some(0)); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.inputs(), [TUPLE_STRING_U8_TY]); + assert_eq!(f.splatted(), Some(0)); } #[test] fn test_not_splat() { - let TypeKind::FnPtr(fn_ptr_ty) = &(const { Type::of::().kind }) else { - panic!(); - }; - let FnPtr { - unsafety: false, - abi: Abi::ExternRust, - inputs: [ty1], - output, - variadic: false, - is_splatted: false, - splatted_index: _, - } = fn_ptr_ty - else { - panic!(); - }; - assert_eq!(output, &UNIT_TY); - assert_eq!(*ty1, TUPLE_STRING_U8_TY); - assert_eq!(fn_ptr_ty.splatted(), None); + let f = const { TypeId::of::().function_ptr() }.unwrap(); + assert_eq!(f.inputs(), [TUPLE_STRING_U8_TY]); + assert_eq!(f.splatted(), None); } diff --git a/library/coretests/tests/mem/type_info.rs b/library/coretests/tests/mem/type_info.rs index f3a69dd857aba..7fe592496f1a7 100644 --- a/library/coretests/tests/mem/type_info.rs +++ b/library/coretests/tests/mem/type_info.rs @@ -271,61 +271,59 @@ fn test_primitives() { #[test] fn test_references() { + use TypeKind::Reference; + // Immutable reference. - match const { Type::of::<&u8>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(!reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&u8>() else { panic!() }; + const { + let ty = TypeId::of::<&u8>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } // Mutable references. - match const { Type::of::<&mut u64>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&mut u64>() else { panic!() }; + const { + let ty = TypeId::of::<&mut u64>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.points_mutably()); } // Wide references. - match const { Type::of::<&dyn Any>() }.kind { - TypeKind::Reference(reference) => { - assert_eq!(reference.pointee, TypeId::of::()); - assert!(!reference.mutable); - } - _ => unreachable!(), + let Type { kind: Reference, .. } = Type::of::<&dyn Any>() else { panic!() }; + const { + let ty = TypeId::of::<&dyn Any>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } } #[test] fn test_pointers() { + use TypeKind::Pointer; + // Immutable pointer. - match const { Type::of::<*const u8>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(!pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*const u8>() else { panic!() }; + const { + let ty = TypeId::of::<*const u8>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } // Mutable pointer. - match const { Type::of::<*mut u64>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*mut u64>() else { panic!() }; + const { + let ty = TypeId::of::<*mut u64>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(ty.points_mutably()); } // Wide pointer. - match const { Type::of::<*const dyn Any>() }.kind { - TypeKind::Pointer(pointer) => { - assert_eq!(pointer.pointee, TypeId::of::()); - assert!(!pointer.mutable); - } - _ => unreachable!(), + let Type { kind: Pointer, .. } = Type::of::<*const dyn Any>() else { panic!() }; + const { + let ty = TypeId::of::<*const dyn Any>(); + assert!(ty.points_to() == Some(TypeId::of::())); + assert!(!ty.points_mutably()); } } diff --git a/library/std/src/thread/join_handle.rs b/library/std/src/thread/join_handle.rs index 93dcc634d2dfa..955fd524e736b 100644 --- a/library/std/src/thread/join_handle.rs +++ b/library/std/src/thread/join_handle.rs @@ -104,7 +104,7 @@ impl JoinHandle { /// Otherwise, it fully waits for the thread to finish, including all destructors /// for thread-local variables that might be running after the main function of the thread. /// - /// In terms of [atomic memory orderings], the completion of the associated + /// In terms of [atomic memory orderings], the completion of the associated /// thread synchronizes with this function returning. In other words, all /// operations performed by that thread [happen /// before](https://doc.rust-lang.org/nomicon/atomics.html#data-accesses) all diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 288e147a26615..754f4a547bd74 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -1018,9 +1018,9 @@ impl Builder<'_> { // Avoid doing this during dry run as that usually means the relevant // compiler is not yet linked/copied properly. // - // Only clear out the directory if we're compiling std; otherwise, we + // Only clear out the directory if we're running Cargo on std; otherwise, we // should let Cargo take care of things for us (via depdep info) - if !self.config.dry_run() && mode == Mode::Std && cmd_kind == Kind::Build { + if !self.config.dry_run() && mode == Mode::Std { build_stamp::clear_if_dirty(self, &out_dir, &self.rustc(compiler)); } diff --git a/src/bootstrap/src/utils/build_stamp.rs b/src/bootstrap/src/utils/build_stamp.rs index 36a3d0772e5ad..f70c92667a5cf 100644 --- a/src/bootstrap/src/utils/build_stamp.rs +++ b/src/bootstrap/src/utils/build_stamp.rs @@ -114,7 +114,13 @@ pub fn clear_if_dirty(builder: &Builder<'_>, dir: &Path, input: &Path) -> bool { let stamp = BuildStamp::new(dir); let mut cleared = false; if mtime(stamp.path()) < mtime(input) { - builder.do_if_verbose(|| println!("Dirty - {}", dir.display())); + builder.do_if_verbose(|| { + println!( + "Removing dirty directory `{}` because `{}` changed", + dir.display(), + input.display(), + ) + }); let _ = fs::remove_dir_all(dir); cleared = true; } else if stamp.path().exists() { diff --git a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs index 83094681e9cc4..92143aaa1fb87 100644 --- a/src/doc/rustc-dev-guide/ci/sembr/src/main.rs +++ b/src/doc/rustc-dev-guide/ci/sembr/src/main.rs @@ -182,6 +182,15 @@ fn lengthen_lines(content: &str, limit: usize) -> String { new_content[new_n] = format!("{line} {}", next_line.trim_start()); new_content.remove(new_n + 1); skip_next = true; + } else { + const SEP: &str = ", "; + let Some((before_comma, after_comma)) = next_line.split_once(SEP) else { continue }; + if line.len() + before_comma.len() < limit - SEP.len() { + new_content[new_n] = format!("{line} {before_comma}{}", SEP.trim_end()); + new_n += 1; + new_content[new_n] = after_comma.to_owned(); + skip_next = true; + } } } new_content.join("\n") + "\n" @@ -326,7 +335,20 @@ fn should_pass() { #[test] #[ignore] -fn split_on_comma() { +fn split_on_comma_of_current_line() { + let original = " +Each derived value has a dependency on other values, which could themselves be either base or +derived. +"; + let expected = " +Each derived value has a dependency on other values, +which could themselves be either base or derived. +"; + assert_eq!(expected, lengthen_lines(original, 100)) +} + +#[test] +fn split_on_comma_of_next_line() { let original = " Because of canonicalization of regions and inference variables, encountering a cycle doesn't mean that we would get an infinite proof tree. @@ -337,3 +359,11 @@ encountering a cycle doesn't mean that we would get an infinite proof tree. "; assert_eq!(expected, lengthen_lines(original, 100)) } + +#[test] +#[ignore] +fn should_split() { + let original = "the queries that we do, as well as the **query DAG**. The"; + let expected = "the queries that we do, as well as the **query DAG**.\nThe\n"; + assert_eq!(expected, comply(original)); +} diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 7b170626619e4..18fea436747c7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -da5114692c9ebe46b869488c5f34f92eb10b98c1 +32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba diff --git a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md index 731ff3287d9fe..079a2b2e00a7f 100644 --- a/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md +++ b/src/doc/rustc-dev-guide/src/queries/incremental-compilation.md @@ -1,41 +1,40 @@ # Incremental compilation The incremental compilation scheme is, in essence, a surprisingly -simple extension to the overall query system. We'll start by describing +simple extension to the overall query system. +We'll start by describing a slightly simplified variant of the real thing – the "basic algorithm" – and then describe some possible improvements. ## The basic algorithm -The basic algorithm is -called the **red-green** algorithm[^salsa]. The high-level idea is -that, after each run of the compiler, we will save the results of all +The basic algorithm is called the **red-green** algorithm[^salsa]. +The high-level idea is that, after each run of the compiler, we will save the results of all the queries that we do, as well as the **query DAG**. The -**query DAG** is a [DAG] that indexes which queries executed which -other queries. So, for example, there would be an [edge] from a query Q1 +**query DAG** is a [DAG] that indexes which queries executed which other queries. +So, for example, there would be an [edge] from a query Q1 to another query Q2 if computing Q1 required computing Q2 (note that -because queries cannot depend on themselves, this results in a DAG and -not a general graph). +because queries cannot depend on themselves, this results in a DAG and not a general graph). [DAG]: https://en.wikipedia.org/wiki/Directed_acyclic_graph > **NOTE**: You might think of a query as simply the definition of a query. -> A thing that you can invoke, a bit like a function, +> A thing that you can invoke, a bit like a function, > and which either returns a cached result or actually executes the code. -> +> > If that's the way you think about queries, -> it's good to know that in the following text, queries will be said to have colours. -> Keep in mind though, that here the word query also refers to a certain invocation of -> the query for a certain input. As you will read later, queries are fingerprinted based -> on their arguments. The result of a query might change when we give it one argument +> it's good to know that in the following text, queries will be said to have colours. +> Keep in mind though, that here the word query also refers to a certain invocation of +> the query for a certain input. As you will read later, queries are fingerprinted based +> on their arguments. The result of a query might change when we give it one argument > and be coloured red, while it stays the same for another argument and is thus green. -> -> In short, the word query is here not just used to mean the definition of a query, +> +> In short, the word query is here not just used to mean the definition of a query, > but also for a specific instance of that query with given arguments. On the next run of the compiler, then, we can sometimes reuse these -query results to avoid re-executing a query. We do this by assigning -every query a **color**: +query results to avoid re-executing a query. +We do this by assigning every query a **color**: - If a query is colored **red**, that means that its result during this compilation has **changed** from the previous compilation. @@ -48,62 +47,57 @@ There are two key insights here: query Q **must** result in the same value as last time and hence need not be re-executed (or else the compiler is not deterministic). - Second, even if some inputs to a query changes, it may be that it - **still** produces the same result as the previous compilation. In - particular, the query may only use part of its input. + **still** produces the same result as the previous compilation. + In particular, the query may only use part of its input. - Therefore, after executing a query, we always check whether it - produced the same result as the previous time. **If it did,** we - can still mark the query as green, and hence avoid re-executing + produced the same result as the previous time. + **If it did,** we can still mark the query as green, and hence avoid re-executing dependent queries. ### The try-mark-green algorithm -At the core of incremental compilation is an algorithm called -"try-mark-green". It has the job of determining the color of a given -query Q (which must not have yet been executed). In cases where Q has -red inputs, determining Q's color may involve re-executing Q so that +At the core of incremental compilation is an algorithm called "try-mark-green". +It has the job of determining the color of a given query Q (which must not have yet been executed). +In cases where Q has red inputs, determining Q's color may involve re-executing Q so that we can compare its output, but if all of Q's inputs are green, then we -can conclude that Q must be green without re-executing it or inspecting -its value at all. In the compiler, this allows us to avoid -deserializing the result from disk when we don't need it, and in fact -enables us to sometimes skip *serializing* the result as well -(see the refinements section below). +can conclude that Q must be green without re-executing it or inspecting its value at all. +In the compiler, this allows us to avoid deserializing the result from disk when we don't need it, +and in fact +enables us to sometimes skip *serializing* the result as well (see the refinements section below). Try-mark-green works as follows: - First check if the query Q was executed during the previous compilation. - - If not, we can just re-execute the query as normal, and assign it the - color of red. + - If not, we can just re-execute the query as normal, and assign it the color of red. - If yes, then load the 'dependent queries' of Q. -- If there is a saved result, then we load the `reads(Q)` vector from the - query DAG. The "reads" is the set of queries that Q executed during - its execution. - - For each query R in `reads(Q)`, we recursively demand the color - of R using try-mark-green. +- If there is a saved result, then we load the `reads(Q)` vector from the query DAG. + The "reads" is the set of queries that Q executed during its execution. + - For each query R in `reads(Q)`, we recursively demand the color of R using try-mark-green. - Note: it is important that we visit each node in `reads(Q)` in same order - as they occurred in the original compilation. See [the section on the - query DAG below](#dag). - - If **any** of the nodes in `reads(Q)` wind up colored **red**, then Q is - dirty. + as they occurred in the original compilation. + See [the section on the query DAG below](#dag). + - If **any** of the nodes in `reads(Q)` wind up colored **red**, then Q is dirty. - We re-execute Q and compare the hash of its result to the hash of the result from the previous compilation. - If the hash has not changed, we can mark Q as **green** and return. - - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. In that - case, we can color Q as **green** and return. + - Otherwise, **all** of the nodes in `reads(Q)` must be **green**. + In that case, we can color Q as **green** and return. ### The query DAG -The query DAG code is stored in -[`compiler/rustc_middle/src/dep_graph`][dep_graph]. Construction of the DAG is done -by instrumenting the query execution. +The query DAG code is stored in [`compiler/rustc_middle/src/dep_graph`][dep_graph]. +Construction of the DAG is done by instrumenting the query execution. -One key point is that the query DAG also tracks ordering; that is, for -each query Q, we not only track the queries that Q reads, we track the -**order** in which they were read. This allows try-mark-green to walk -those queries back in the same order. This is important because once a -subquery comes back as red, we can no longer be sure that Q will continue -along the same path as before. That is, imagine a query like this: +One key point is that the query DAG also tracks ordering; that is, for each query Q, +we not only track the queries that Q reads; +we also track the **order** in which they were read. +This allows try-mark-green to walk those queries back in the same order. +This is important because once a subquery comes back as red, +we can no longer be sure that Q will continue +along the same path as before. +That is, imagine a query like this: ```rust,ignore fn main_query(tcx) { @@ -115,15 +109,15 @@ fn main_query(tcx) { } ``` -Now imagine that in the first compilation, `main_query` starts by -executing `subquery1`, and this returns true. In that case, the next -query `main_query` executes will be `subquery2`, and `subquery3` will +Now imagine that in the first compilation, `main_query` starts by executing `subquery1`, +and this returns true. +In that case, the next query `main_query` executes will be `subquery2`, and `subquery3` will not be executed at all. But now imagine that in the **next** compilation, the input has -changed such that `subquery1` returns **false**. In this case, `subquery2` -would never execute. If try-mark-green were to visit `reads(main_query)` out -of order, however, it might visit `subquery2` before `subquery1`, and hence +changed such that `subquery1` returns **false**. In this case, `subquery2` would never execute. +If try-mark-green were to visit `reads(main_query)` out of order, +however, it might visit `subquery2` before `subquery1`, and hence execute it. This can lead to ICEs and other problems in the compiler. @@ -132,28 +126,26 @@ This can lead to ICEs and other problems in the compiler. ## Improvements to the basic algorithm In the description of the basic algorithm, we said that at the end of -compilation we would save the results of all the queries that were -performed. In practice, this can be quite wasteful – many of those -results are very cheap to recompute, and serializing and deserializing -them is not a particular win. In practice, what we would do is to save -**the hashes** of all the subqueries that we performed. Then, in select cases, -we **also** save the results. - -This is why the incremental algorithm separates computing the -**color** of a node, which often does not require its value, from -computing the **result** of a node. Computing the result is done via a simple -algorithm like so: - -- Check if a saved result for Q is available. If so, compute the color of Q. +compilation we would save the results of all the queries that were performed. +In practice, this can be quite wasteful – many of those results are very cheap to recompute, +and serializing and deserializing them is not a particular win. +In practice, what we would do is to save **the hashes** of all the subqueries that we performed. +Then, in select cases, we **also** save the results. + +This is why the incremental algorithm separates computing the **color** of a node, +which often does not require its value, from +computing the **result** of a node. +Computing the result is done via a simple algorithm like so: + +- Check if a saved result for Q is available. + If so, compute the color of Q. If Q is green, deserialize and return the saved result. - Otherwise, execute Q. - - We can then compare the hash of the result and color Q as green if - it did not change. + - We can then compare the hash of the result and color Q as green if it did not change. ## Resources The initial design document can be found [here][initial-design], which expands -on the memoization details, provides more high-level overview and motivation -for this system. +on the memoization details, provides more high-level overview and motivation for this system. # Footnotes diff --git a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md index c1a4373f7dac6..443bcea4ee323 100644 --- a/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md +++ b/src/doc/rustc-dev-guide/src/queries/query-evaluation-model-in-detail.md @@ -1,9 +1,9 @@ # The Query Evaluation Model in detail This chapter provides a deeper dive into the abstract model queries are built on. -It does not go into implementation details but tries to explain -the underlying logic. The examples here, therefore, have been stripped down and -simplified and don't directly reflect the compilers internal APIs. +It does not go into implementation details but tries to explain the underlying logic. +The examples here, therefore, have been stripped down and simplified, +and don't directly reflect compiler internal APIs. ## What is a query? @@ -12,9 +12,9 @@ and queries are the way of asking the compiler questions about it, i.e. we "query" the compiler's "database" for facts. However, there's something special to this compiler database: It starts out empty -and is filled on-demand when queries are executed. Consequently, a query must -know how to compute its result if the database does not contain it yet. For -doing so, it can access other queries and certain input values that the database +and is filled on-demand when queries are executed. +Consequently, a query must know how to compute its result if the database does not contain it yet. +For doing so, it can access other queries and certain input values that the database is pre-filled with on creation. A query thus consists of the following things: @@ -26,14 +26,12 @@ A query thus consists of the following things: computed if it isn't already present in the database. As an example, the name of the `type_of` query is `type_of`, its query key is a -`DefId` identifying the item we want to know the type of, the result type is -`Ty<'tcx>`, and the provider is a function that, given the query key and access -to the rest of the database, can compute the type of the item identified by the -key. +`DefId` identifying the item we want to know the type of, the result type is `Ty<'tcx>`, +and the provider is a function that, given the query key and access +to the rest of the database, can compute the type of the item identified by the key. -So in some sense a query is just a function that maps the query key to the -corresponding result. However, we have to apply some restrictions in order for -this to be sound: +So in some sense a query is just a function that maps the query key to the corresponding result. +However, we have to apply some restrictions in order for this to be sound: - The key and result must be immutable values. - The provider function must be a pure function in the sense that for the same @@ -41,9 +39,11 @@ this to be sound: - The only parameters a provider function takes are the key and a reference to the "query context" (which provides access to the rest of the "database"). -The database is built up lazily by invoking queries. The query providers will -invoke other queries, for which the result is either already cached or computed -by calling another query provider. These query provider invocations +The database is built up lazily by invoking queries. +The query providers will invoke other queries, +for which the result is either already cached or computed +by calling another query provider. +These query provider invocations conceptually form a directed acyclic graph (DAG) at the leaves of which are input values that are already known when the query context is created. @@ -56,12 +56,12 @@ will cache the result in an internal table and, when the query is invoked with the same query key again, will return the result from the cache instead of running the provider again. -This caching is crucial for making the query engine efficient. Without -memoization the system would still be sound (that is, it would yield the same +This caching is crucial for making the query engine efficient. +Without memoization, the system would still be sound (that is, it would yield the same results) but the same computations would be done over and over again. -Memoization is one of the main reasons why query providers have to be pure -functions. If calling a provider function could yield different results for +Memoization is one of the main reasons why query providers have to be pure functions. +If calling a provider function could yield different results for each invocation (because it accesses some global mutable state) then we could not memoize the result. @@ -69,9 +69,10 @@ not memoize the result. ## Input data -When the query context is created, it is still empty: No queries have been -executed, no results are cached. But the context already provides access to -"input" data, i.e. pieces of immutable data that were computed before the +When the query context is created, it is still empty: No queries have been executed, +no results are cached. +But the context already provides access to "input" data, +i.e. pieces of immutable data that were computed before the context was created and that queries can access to do their computations. As of January 2021, this input data consists mainly of @@ -85,18 +86,19 @@ result from (remember, query providers only have access to other queries and the context but not any other outside state or information). For a query provider, input data and results of other queries look exactly the -same: It just tells the context "give me the value of X". Because input data -is immutable, the provider can rely on it being the same across +same: It just tells the context "give me the value of X". +Because input data is immutable, the provider can rely on it being the same across different query invocations, just as is the case for query results. ## An example execution trace of some queries -How does this DAG of query invocations come into existence? At some point -the compiler driver will create the, as yet empty, query context. It will then, -from outside of the query system, invoke the queries it needs to perform its -task. This looks something like the following: +How does this DAG of query invocations come into existence? +At some point the compiler driver will create the, as yet empty, query context. +It will then, +from outside of the query system, invoke the queries it needs to perform its task. +This looks something like the following: ```rust,ignore fn compile_crate() { @@ -124,9 +126,8 @@ fn type_check_crate_provider(tcx, _key: ()) { ``` We see that the `type_check_crate` query accesses input data -(`tcx.hir_map.list_of_items()`) and invokes other queries -(`type_check_item`). The `type_check_item` -invocations will themselves access input data and/or invoke other queries, +(`tcx.hir_map.list_of_items()`) and invokes other queries (`type_check_item`). +The `type_check_item` invocations will themselves access input data and/or invoke other queries, so that in the end the DAG of query invocations will be built up backwards from the node that was initially executed: @@ -146,8 +147,8 @@ from the node that was initially executed: ``` We also see that often a query result can be read from the cache: -`type_of(bar)` was computed for `type_check_item(foo)` so when -`type_check_item(bar)` needs it, it is already in the cache. +`type_of(bar)` was computed for `type_check_item(foo)` so when `type_check_item(bar)` needs it, +it is already in the cache. Query results stay cached in the query context as long as the context lives. So if the compiler driver invoked another query later on, the above graph @@ -157,8 +158,8 @@ would still exist and already executed queries would not have to be re-done. ## Cycles -Earlier we stated that query invocations form a DAG. However, it would be easy -to form a cyclic graph by, for example, having a query provider like the +Earlier we stated that query invocations form a DAG. +However, it would be easy to form a cyclic graph by, for example, having a query provider like the following: ```rust,ignore @@ -169,24 +170,25 @@ fn cyclic_query_provider(tcx, key) -> u32 { ``` Since query providers are regular functions, this would behave much as expected: -Evaluation would get stuck in an infinite recursion. A query like this would not -be very useful either. However, sometimes certain kinds of invalid user input -can result in queries being called in a cyclic way. The query engine includes -a check for cyclic invocations of queries with the same input arguments. -And, because cycles are an irrecoverable error, will abort execution with a +Evaluation would get stuck in an infinite recursion. +A query like this would not be very useful either. +However, sometimes certain kinds of invalid user input +can result in queries being called in a cyclic way. +The query engine includes a check for cyclic invocations of queries with the same input arguments. +And, because cycles are an irrecoverable error, will abort execution with a "cycle error" message that tries to be human readable. -At some point the compiler had a notion of "cycle recovery", that is, one could -"try" to execute a query and if it ended up causing a cycle, proceed in some -other fashion. However, this was later removed because it is not entirely -clear what the theoretical consequences of this are, especially regarding -incremental compilation. +At some point the compiler had a notion of "cycle recovery". +That is, one could "try" to execute a query, +and if it ended up causing a cycle, proceed in some other fashion. +However, this was later removed because it is not entirely +clear what the theoretical consequences of this are, especially regarding incremental compilation. ## "Steal" Queries -Some queries have their result wrapped in a `Steal` struct. These queries -behave exactly the same as regular with one exception: Their result is expected +Some queries have their result wrapped in a `Steal` struct. +These queries behave exactly the same as regular with one exception: Their result is expected to be "stolen" out of the cache at some point, meaning some other part of the program is taking ownership of it and the result cannot be accessed anymore. @@ -194,18 +196,18 @@ This stealing mechanism exists purely as a performance optimization because some result values are too costly to clone (e.g. the MIR of a function). It seems like result stealing would violate the condition that query results must be immutable (after all we are moving the result value out of the cache) but it is -OK as long as the mutation is not observable. This is achieved by two things: +OK as long as the mutation is not observable. +This is achieved by two things: - Before a result is stolen, we make sure to eagerly run all queries that - might ever need to read that result. This has to be done manually by calling - those queries. + might ever need to read that result. + This has to be done manually by calling those queries. - Whenever a query tries to access a stolen result, we make an ICE (Internal Compiler Error) so that such a condition cannot go unnoticed. This is not an ideal setup because of the manual intervention needed, so it -should be used sparingly and only when it is well known which queries might -access a given result. In practice, however, stealing has not turned out to be -much of a maintenance burden. +should be used sparingly and only when it is well known which queries might access a given result. +In practice, however, stealing has not turned out to be much of a maintenance burden. To summarize: "Steal queries" break some of the rules in a controlled way. There are checks in place that make sure that nothing can go silently wrong. diff --git a/src/doc/rustc-dev-guide/src/queries/salsa.md b/src/doc/rustc-dev-guide/src/queries/salsa.md index dc7160edc22cd..fcd67de72b206 100644 --- a/src/doc/rustc-dev-guide/src/queries/salsa.md +++ b/src/doc/rustc-dev-guide/src/queries/salsa.md @@ -2,10 +2,9 @@ This chapter is based on the explanation given by Niko Matsakis in this [video](https://www.youtube.com/watch?v=_muY4HjSqVw) about -[Salsa](https://github.com/salsa-rs/salsa). To find out more you may -want to watch [Salsa In More -Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko -Matsakis. +[Salsa](https://github.com/salsa-rs/salsa). +To find out more you may want to watch [Salsa In More +Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko Matsakis. > As of November 2022, although Salsa is inspired by (among > other things) rustc's query system, it is not used directly in rustc. It @@ -19,9 +18,9 @@ Matsakis. ## What is Salsa? -Salsa is a library for incremental recomputation. This means it allows reusing -computations that were already done in the past to increase the efficiency -of future computations. +Salsa is a library for incremental recomputation. +This means it allows reusing computations that were already done in the past +to increase the efficiency of future computations. The objectives of Salsa are: * Provide that functionality in an automatic way, so reusing old computations @@ -31,10 +30,11 @@ The objectives of Salsa are: Salsa's actual model is much richer, allowing many kinds of inputs and many different outputs. For example, integrating Salsa with an IDE could mean that -the inputs could be manifests (`Cargo.toml`, `rust-toolchain.toml`), entire -source files (`foo.rs`), snippets and so on. The outputs of such an integration -could range from a binary executable, to lints, types (for example, if a user -selects a certain variable and wishes to see its type), completions, etc. +the inputs could be manifests (`Cargo.toml`, `rust-toolchain.toml`), +entire source files (`foo.rs`), snippets, and so on. +The outputs of such an integration could range from a binary executable, +to lints, types (for example, if a user selects a certain variable and wishes to see its type), +completions, etc. ## How does it work? @@ -45,26 +45,27 @@ Then Salsa has to also identify intermediate, "derived" values, which are something that the library produces, but, for each derived value there's a "pure" function that computes the derived value. -For example, there might be a function `ast(x: Path) -> AST`. The produced -Abstract Syntax Tree (`AST`) isn't a final value, it's an intermediate value -that the library would use for the computation. +For example, there might be a function `ast(x: Path) -> AST`. +The produced Abstract Syntax Tree (`AST`) isn't a final value; +it's an intermediate value that the library would use for the computation. This means that when you try to compute with the library, Salsa is going to compute various derived values, and eventually read the input and produce the result for the asked computation. -In the course of computing, Salsa tracks which inputs were accessed and which -values are derived. This information is used to determine what's going to +In the course of computing, Salsa tracks which inputs were accessed and which values are derived. +This information is used to determine what's going to happen when the inputs change: are the derived values still valid? This doesn't necessarily mean that each computation downstream from the input -is going to be checked, which could be costly. Salsa only needs to check each -downstream computation until it finds one that isn't changed. At that point, it -won't check other derived computations since they wouldn't need to change. +is going to be checked, which could be costly. +Salsa only needs to check each downstream computation until it finds one that isn't changed. +At that point, it won't check other derived computations since they wouldn't need to change. -It's helpful to think about this as a graph with nodes. Each derived value -has a dependency on other values, which could themselves be either base or -derived. Base values don't have a dependency. +It's helpful to think about this as a graph with nodes. +Each derived value has a dependency on other values, which could themselves be either base or +derived. +Base values don't have a dependency. ```ignore I <- A <- C ... @@ -72,49 +73,52 @@ I <- A <- C ... J <- B <--+ ``` -When an input `I` changes, the derived value `A` could change. The derived -value `B`, which does not depend on `I`, `A`, or any value derived from `A` or -`I`, is not subject to change. Therefore, Salsa can reuse the computation done -for `B` in the past, without having to compute it again. - -The computation could also terminate early. Keeping the same graph as before, -say that input `I` has changed in some way (and input `J` hasn't), but when -computing `A` again, it's found that `A` hasn't changed from the previous -computation. This leads to an "early termination", because there's no need to -check if `C` needs to change, since both `C` direct inputs, `A` and `B`, +When an input `I` changes, the derived value `A` could change. +The derived value `B`, which does not depend on `I`, `A`, or any value derived from `A` or +`I`, is not subject to change. +Therefore, Salsa can reuse the computation done for `B` in the past, +without having to compute it again. + +The computation could also terminate early. +Keeping the same graph as before, +say that input `I` has changed in some way (and input `J` hasn't), but when computing `A` again, +it's found that `A` hasn't changed from the previous +computation. +This leads to an "early termination", because there's no need to check if `C` needs to change, +since both `C` direct inputs, `A` and `B`, haven't changed. ## Key Salsa concepts ### Query -A query is some value that Salsa can access in the course of computation. Each -query can have a number of keys (from 0 to many), and all queries have a -result, akin to functions. `0-key` queries are called "input" queries. +A query is some value that Salsa can access in the course of computation. +Each query can have a number of keys (from 0 to many), and all queries have a +result, akin to functions. +`0-key` queries are called "input" queries. ### Database The database is basically the context for the entire computation, it's meant to store Salsa's internal state, all intermediate values for each query, and -anything else that the computation might need. The database must know all the -queries the library is going to do before it can be built, but they don't need +anything else that the computation might need. +The database must know all the queries the library is going to do before it can be built, +but they don't need to be specified in the same place. -After the database is formed, it can be accessed with queries that are very -similar to functions. Since each query's result is stored in the database, when -a query is invoked `N`-times, it will return `N`-**cloned** results, without having -to recompute the query (unless the input has changed in such a way that it -warrants recomputation). +After the database is formed, it can be accessed with queries that are very similar to functions. +Since each query's result is stored in the database, when a query is invoked `N`-times, +it will return `N`-**cloned** results, without having +to recompute the query (unless the input has changed in such a way that it warrants recomputation). For each input query (`0-key`), a "set" method is generated, allowing the user to -change the output of such query, and trigger previous memoized values to be -potentially invalidated. +change the output of such query, and trigger previous memoized values to be potentially invalidated. ### Query Groups A query group is a set of queries which have been defined together as a unit. -The database is formed by combining query groups. Query groups are akin to -"Salsa modules". +The database is formed by combining query groups. +Query groups are akin to "Salsa modules". A set of queries in a query group are just a set of methods in a trait. @@ -146,8 +150,7 @@ pub trait Inputs { ``` To create a **derived** query group, one must specify which other query groups -this one depends on by specifying them as supertraits, as seen in the following -example: +this one depends on by specifying them as supertraits, as seen in the following example: ```rust,ignore /// This query group is going to contain queries that depend on derived values. @@ -162,9 +165,9 @@ pub trait Parser: Inputs { } ``` -When creating a derived query the implementation of said query must be defined -outside the trait. The definition must take a database parameter as an `impl -Trait` (or `dyn Trait`), where trait is the query group that the definition +When creating a derived query, the implementation of said query must be defined outside the trait. +The definition must take a database parameter as an `impl Trait` (or `dyn Trait`), +where trait is the query group that the definition belongs to, in addition to the other keys. ```rust,ignore @@ -187,8 +190,9 @@ Eventually, after all the query groups have been defined, the database can be created by declaring a `struct`. To specify which query groups are going to be part of the database an `attribute` -(`#[salsa::database(...)]`) must be added. The argument of said `attribute` is a -list of `identifiers`, specifying the query groups **storages**. +(`#[salsa::database(...)]`) must be added. +The argument of said `attribute` is a list of `identifiers`, +specifying the query groups **storages**. ```rust,ignore ///This attribute specifies which query groups are going to be in the database diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 617ad701d129e..22735c124fb43 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -240,8 +240,8 @@ See also [Debuginfo tests](compiletest.md#debuginfo-tests) for directives for ig [remote testing]: running.md#running-tests-on-a-remote-machine [parallel frontend]: compiletest.md#parallel-frontend [compare modes]: ui.md#compare-modes -[`x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/ab3dba92db355b8d97db915a2dca161a117e959c/src/ci/docker/host-x86_64/x86_64-gnu-debug/Dockerfile#L32 -[`aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/20c909ff9cdd88d33768a4ddb8952927a675b0ad/src/ci/docker/host-aarch64/aarch64-gnu-debug/Dockerfile#L32 +[`test-x86_64-gnu-debug`]: https://github.com/rust-lang/rust/blob/32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba/src/ci/docker/host-x86_64/test-x86_64-gnu-debug/Dockerfile#L32 +[`test-aarch64-gnu-debug`]: https://github.com/rust-lang/rust/blob/32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba/src/ci/docker/host-aarch64/test-aarch64-gnu-debug/Dockerfile#L32 ### Affecting how tests are built diff --git a/tests/coverage/comptime.cov-map b/tests/coverage/comptime.cov-map new file mode 100644 index 0000000000000..30f91da050f6f --- /dev/null +++ b/tests/coverage/comptime.cov-map @@ -0,0 +1,10 @@ +Function name: comptime::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 0b, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => $DIR/comptime.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 11, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/comptime.coverage b/tests/coverage/comptime.coverage new file mode 100644 index 0000000000000..1ff44169babb2 --- /dev/null +++ b/tests/coverage/comptime.coverage @@ -0,0 +1,12 @@ + LL| |#![feature(rustc_attrs)] + LL| |//@ edition: 2024 + LL| | + LL| |// Check that instrumenting a crate with a comptime function doesn't ICE. + LL| |// (The function itself doesn't need to be instrumented, and probably shouldn't be.) + LL| |// Regression test for . + LL| | + LL| |#[rustc_comptime] + LL| |fn comptime_fn() {} + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/comptime.rs b/tests/coverage/comptime.rs new file mode 100644 index 0000000000000..4891051b0076d --- /dev/null +++ b/tests/coverage/comptime.rs @@ -0,0 +1,11 @@ +#![feature(rustc_attrs)] +//@ edition: 2024 + +// Check that instrumenting a crate with a comptime function doesn't ICE. +// (The function itself doesn't need to be instrumented, and probably shouldn't be.) +// Regression test for . + +#[rustc_comptime] +fn comptime_fn() {} + +fn main() {} diff --git a/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs b/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs new file mode 100644 index 0000000000000..be49be470091e --- /dev/null +++ b/tests/run-make/parallel-reproducible-inline-asm-cookie/inline-asm-cookie-issue-150451.rs @@ -0,0 +1,9 @@ +use std::thread; + +fn _main() { + let _t1 = thread::spawn(|| { + for _ in 0..100 { + println!("test"); + } + }); +} diff --git a/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs b/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs new file mode 100644 index 0000000000000..62ad5a46af860 --- /dev/null +++ b/tests/run-make/parallel-reproducible-inline-asm-cookie/rmake.rs @@ -0,0 +1,42 @@ +//@ needs-target-std +//@ ignore-cross-compile +//@ ignore-windows-gnu +// GNU Linker for Windows is non-deterministic. (from `reproducible-build-2` test in this suite) + +use std::rc::Rc; + +use run_make_support::{bin_name, is_windows_msvc, rfs, run_in_tmpdir, rustc}; + +/// Test that parallel compiler produces identical binaries. +fn main() { + const FILE_NAME: &str = "inline-asm-cookie-issue-150451"; + let bin_name = bin_name(FILE_NAME); + + let mut reference = None; + + for _ in 0..10 { + // Tmp dir as previous runs affect output binary on windows. + run_in_tmpdir(|| { + let mut rustc = rustc(); + rustc + .input(format!("{FILE_NAME}.rs")) + .arg("--crate-type=lib") + .arg("-Zthreads=3") + .arg("-Clink-dead-code=true") + .arg("-Copt-level=0") + .arg("-Cembed-bitcode=true") + .output(&bin_name); + + if is_windows_msvc() { + rustc.arg("-Clink-arg=/Brepro"); + } + + rustc.run(); + + let current = Rc::new(rfs::read(&bin_name)); + reference.get_or_insert(Rc::clone(¤t)); + + assert_eq!(Some(current), reference); + }); + } +} diff --git a/tests/rustdoc-ui/expect-item-after-attribute.rs b/tests/rustdoc-ui/expect-item-after-attribute.rs new file mode 100644 index 0000000000000..c53395caa8efc --- /dev/null +++ b/tests/rustdoc-ui/expect-item-after-attribute.rs @@ -0,0 +1,36 @@ +//@ compile-flags: --test --test-args=--test-threads=1 +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ failure-status: 101 + +//! ``` +//! #[should_panic] +//! ``` + +//! ``` +//! fn main() { +//! #[should_panic] +//! } +//! ``` + +//! ``` +//! fn main() { } +//! #[should_panic] +//! ``` + +//! ``` +//! let x = 0; #[should_panic] +//! ``` + +//! ``` +//! let x = 0; //! assert!(true); +//! ``` + +/// ``` +/// /// doc comment +/// ``` +struct Test; + +/// ``` +/// #[cfg(true)] { +/// } /// ``` +pub fn wtf() {} diff --git a/tests/rustdoc-ui/expect-item-after-attribute.stdout b/tests/rustdoc-ui/expect-item-after-attribute.stdout new file mode 100644 index 0000000000000..f853d6facf1a7 --- /dev/null +++ b/tests/rustdoc-ui/expect-item-after-attribute.stdout @@ -0,0 +1,104 @@ + +running 7 tests +test $DIR/expect-item-after-attribute.rs - (line 13) ... FAILED +test $DIR/expect-item-after-attribute.rs - (line 17) ... FAILED +test $DIR/expect-item-after-attribute.rs - (line 20) ... FAILED +test $DIR/expect-item-after-attribute.rs - (line 5) ... FAILED +test $DIR/expect-item-after-attribute.rs - (line 8) ... FAILED +test $DIR/expect-item-after-attribute.rs - Test (line 28) ... FAILED +test $DIR/expect-item-after-attribute.rs - wtf (line 33) ... FAILED + +failures: + +---- $DIR/expect-item-after-attribute.rs - (line 13) stdout ---- +error: expected item after attributes + --> $DIR/expect-item-after-attribute.rs:15:1 + | +LL | #[should_panic] + | ^^^^^^^^^^^^^^^ expected an item after this + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - (line 17) stdout ---- +error: expected item, found keyword `let` + --> $DIR/expect-item-after-attribute.rs:18:1 + | +LL | let x = 0; #[should_panic] + | ^^^ + | | + | `let` cannot be used for global variables + | help: consider using `static` or `const` instead of `let` + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - (line 20) stdout ---- +error: expected item, found keyword `let` + --> $DIR/expect-item-after-attribute.rs:21:1 + | +LL | let x = 0; //! assert!(true); + | ^^^ + | | + | `let` cannot be used for global variables + | help: consider using `static` or `const` instead of `let` + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - (line 5) stdout ---- +error: expected item after attributes + --> $DIR/expect-item-after-attribute.rs:6:1 + | +LL | #[should_panic] + | ^^^^^^^^^^^^^^^ expected an item after this + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - (line 8) stdout ---- +error: expected statement after outer attribute + --> $DIR/expect-item-after-attribute.rs:10:5 + | +LL | #[should_panic] + | ^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - Test (line 28) stdout ---- +error: expected item after doc comment + --> $DIR/expect-item-after-attribute.rs:29:1 + | +LL | /// doc comment + | ^^^^^^^^^^^^^^^ this doc comment doesn't document anything + +error: aborting due to 1 previous error + +Couldn't compile the test. +---- $DIR/expect-item-after-attribute.rs - wtf (line 33) stdout ---- +error: expected item after attributes + --> $DIR/expect-item-after-attribute.rs:34:1 + | +LL | #[cfg(true)] { + | ^^^^^^^^^^^^ expected an item after this + +error: aborting due to 1 previous error + +Couldn't compile the test. + +failures: + $DIR/expect-item-after-attribute.rs - (line 13) + $DIR/expect-item-after-attribute.rs - (line 17) + $DIR/expect-item-after-attribute.rs - (line 20) + $DIR/expect-item-after-attribute.rs - (line 5) + $DIR/expect-item-after-attribute.rs - (line 8) + $DIR/expect-item-after-attribute.rs - Test (line 28) + $DIR/expect-item-after-attribute.rs - wtf (line 33) + +test result: FAILED. 0 passed; 7 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/ui/asm/aarch64/srcloc.rs b/tests/ui/asm/aarch64/srcloc.rs index 91a2ef3514aee..b5e77c4023970 100644 --- a/tests/ui/asm/aarch64/srcloc.rs +++ b/tests/ui/asm/aarch64/srcloc.rs @@ -1,7 +1,7 @@ //@ add-minicore //@ build-fail //@ needs-asm-support -//@ compile-flags: --target aarch64-unknown-linux-gnu -Ccodegen-units=1 +//@ compile-flags: --target aarch64-unknown-linux-gnu -Ccodegen-units=1 -Cembed-bitcode=false -Clto=no //@ needs-llvm-components: aarch64 //@ ignore-backends: gcc #![crate_type = "lib"] diff --git a/tests/ui/asm/inline-syntax.arm.stderr b/tests/ui/asm/inline-syntax.arm.stderr index 5b193d26c8776..315f97bb09de9 100644 --- a/tests/ui/asm/inline-syntax.arm.stderr +++ b/tests/ui/asm/inline-syntax.arm.stderr @@ -6,15 +6,6 @@ note: instantiated into assembly here LL | .intel_syntax noprefix | ^ -error: unknown directive - | -note: instantiated into assembly here - --> :1:1 - | -LL | .intel_syntax noprefix - | ^ - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error: unknown directive --> $DIR/inline-syntax.rs:21:15 | @@ -87,5 +78,5 @@ note: instantiated into assembly here LL | .intel_syntax noprefix | ^ -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/tests/ui/asm/inline-syntax.rs b/tests/ui/asm/inline-syntax.rs index 63395c1096c09..d7c9fc8972cb7 100644 --- a/tests/ui/asm/inline-syntax.rs +++ b/tests/ui/asm/inline-syntax.rs @@ -1,9 +1,9 @@ //@ add-minicore //@ revisions: x86_64 arm -//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu -Cembed-bitcode=false -Clto=no //@[x86_64] check-pass //@[x86_64] needs-llvm-components: x86 -//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf +//@[arm] compile-flags: --target armv7-unknown-linux-gnueabihf -Cembed-bitcode=false -Clto=no //@[arm] build-fail //@[arm] needs-llvm-components: arm //@[arm] min-llvm-version: 23 @@ -49,4 +49,3 @@ global_asm!(".intel_syntax noprefix", "nop"); // Global assembly errors don't have line numbers, so no error on ARM. //[arm]~? ERROR unknown directive -//[arm]~? ERROR unknown directive diff --git a/tests/ui/asm/riscv/riscv32e-registers.rs b/tests/ui/asm/riscv/riscv32e-registers.rs index a5f4151b2c80a..77a2d92c3736b 100644 --- a/tests/ui/asm/riscv/riscv32e-registers.rs +++ b/tests/ui/asm/riscv/riscv32e-registers.rs @@ -4,7 +4,7 @@ //@ build-fail //@ revisions: riscv32e_llvm23 riscv32em_llvm23 riscv32emc_llvm23 //@ revisions: riscv32e_llvm24 riscv32em_llvm24 riscv32emc_llvm24 -//@ compile-flags: --crate-type=rlib +//@ compile-flags: --crate-type=rlib -Cembed-bitcode=false -Clto=no //@ [riscv32e_llvm23] needs-llvm-components: riscv //@ [riscv32e_llvm23] compile-flags: --target=riscv32e-unknown-none-elf //@ [riscv32e_llvm23] max-llvm-major-version: 23 diff --git a/tests/ui/asm/x86_64/srcloc.rs b/tests/ui/asm/x86_64/srcloc.rs index e73854acf1522..9bbd20340e3ab 100644 --- a/tests/ui/asm/x86_64/srcloc.rs +++ b/tests/ui/asm/x86_64/srcloc.rs @@ -1,6 +1,6 @@ //@ add-minicore //@ build-fail -//@ compile-flags: --target x86_64-unknown-linux-gnu -Ccodegen-units=1 +//@ compile-flags: --target x86_64-unknown-linux-gnu -Ccodegen-units=1 -Cembed-bitcode=false -Clto=no //@ needs-llvm-components: x86 //@ ignore-backends: gcc #![crate_type = "lib"] diff --git a/tests/ui/async-await/spurious-static-bound-issue-115376.rs b/tests/ui/async-await/spurious-static-bound-issue-115376.rs new file mode 100644 index 0000000000000..42cd388ab7eec --- /dev/null +++ b/tests/ui/async-await/spurious-static-bound-issue-115376.rs @@ -0,0 +1,8 @@ +//@ edition: 2021 + +async fn test(_: &u8) { + let _: &'static T; + //~^ ERROR the parameter type `T` may not live long enough +} + +fn main() {} diff --git a/tests/ui/async-await/spurious-static-bound-issue-115376.stderr b/tests/ui/async-await/spurious-static-bound-issue-115376.stderr new file mode 100644 index 0000000000000..6292823029de1 --- /dev/null +++ b/tests/ui/async-await/spurious-static-bound-issue-115376.stderr @@ -0,0 +1,17 @@ +error[E0310]: the parameter type `T` may not live long enough + --> $DIR/spurious-static-bound-issue-115376.rs:4:12 + | +LL | let _: &'static T; + | ^^^^^^^^^^ + | | + | the parameter type `T` must be valid for the static lifetime... + | ...so that the type `T` will meet its required lifetime bounds + | +help: consider adding an explicit lifetime bound + | +LL | async fn test(_: &u8) { + | +++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs index 4fdf5470feac6..0edabe009acdb 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.rs @@ -14,7 +14,6 @@ impl Foo { //~| ERROR the parameter type `impl for<'a> Fn(&'a usize) -> Box` may not live long enough //~| ERROR the parameter type `I` may not live long enough //~| ERROR the parameter type `I` may not live long enough - //~| ERROR the parameter type `I` may not live long enough //~| ERROR `f` does not live long enough } } diff --git a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr index df86ce79f09c7..cbeb8fba8d226 100644 --- a/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr +++ b/tests/ui/borrowck/unconstrained-closure-lifetime-generic.stderr @@ -84,19 +84,6 @@ help: consider adding an explicit lifetime bound LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { | +++++++++ -error[E0311]: the parameter type `I` may not live long enough - --> $DIR/unconstrained-closure-lifetime-generic.rs:10:35 - | -LL | pub fn ack(&mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | --------- the parameter type `I` must be valid for the anonymous lifetime defined here... -LL | self.bar = Box::new(|baz| Box::new(f(baz))); - | ^^^^^^^^^^^^^^^^ ...so that the type `I` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | pub fn ack<'a, I: 'a>(&'a mut self, f: impl for<'a> Fn(&'a usize) -> Box) { - | +++ ++++ ++ - error[E0597]: `f` does not live long enough --> $DIR/unconstrained-closure-lifetime-generic.rs:10:44 | @@ -113,7 +100,7 @@ LL | } | = note: due to object lifetime defaults, `Box Fn(&'a usize) -> Box<(dyn Any + 'a)>>` actually means `Box<(dyn for<'a> Fn(&'a usize) -> Box<(dyn Any + 'a)> + 'static)>` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0310, E0311, E0597. +Some errors have detailed explanations: E0310, E0597. For more information about an error, try `rustc --explain E0310`. diff --git a/tests/ui/consts/const-eval/do_not_const_check.rs b/tests/ui/consts/const-eval/do_not_const_check.rs new file mode 100644 index 0000000000000..ced2557bffd19 --- /dev/null +++ b/tests/ui/consts/const-eval/do_not_const_check.rs @@ -0,0 +1,25 @@ +//! Ensure that we refuse to run a do_not_const_check function, even if the body *would* const-check +//! at the moment. +#![feature(rustc_attrs, intrinsics)] + +#[rustc_do_not_const_check] +const fn mostly_harmless() {} + +const _: () = { + mostly_harmless(); //~ERROR: calling non-const function +}; + +// Also ensure the same happens with intrinsics. +// Here we need some intrinsic that the interpreter does *not* have a native implementation for. +// Let's hope nobody adds one... +#[rustc_intrinsic] +#[rustc_do_not_const_check] +pub const fn integer_min(a: T, b: T) -> T { + a +} + +const _: () = { + integer_min(0, 1); //~ERROR: calling non-const function +}; + +fn main() {} diff --git a/tests/ui/consts/const-eval/do_not_const_check.stderr b/tests/ui/consts/const-eval/do_not_const_check.stderr new file mode 100644 index 0000000000000..507999df218d1 --- /dev/null +++ b/tests/ui/consts/const-eval/do_not_const_check.stderr @@ -0,0 +1,15 @@ +error[E0080]: calling non-const function `mostly_harmless` + --> $DIR/do_not_const_check.rs:9:5 + | +LL | mostly_harmless(); + | ^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error[E0080]: calling non-const function `integer_min::` + --> $DIR/do_not_const_check.rs:22:5 + | +LL | integer_min(0, 1); + | ^^^^^^^^^^^^^^^^^ evaluation of `_` failed here + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0080`.