diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 0d0f5e405d1f4..5318f9a507a50 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -59,7 +59,10 @@ # toolchain or changing LLVM locally, you probably want to leave this enabled. # # Set this to `true` to download if CI llvm available otherwise it builds -# from `src/llvm-project`. +# from `src/llvm-project`. If you set it to `true`, it's safe and time-saving to run +# `git submodule deinit src/llvm-project` to avoid git updating the llvm-project submodule +# when building compiler locally. +# # # Set this to `"if-unchanged"` to download only if the llvm-project has not # been modified. You can also use this if you are unsure whether you're on a diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f904b2b670a29..524f8b054cb49 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1929,7 +1929,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// ControlFlow::Break(residual) => /// #[allow(unreachable_code)] /// // If there is an enclosing `try {...}`: - /// break 'catch_target Try::from_residual(residual), + /// break 'catch_target Residual::into_try_type(residual), /// // Otherwise: /// return Try::from_residual(residual), /// } @@ -1979,7 +1979,11 @@ impl<'hir> LoweringContext<'_, 'hir> { let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident); let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid); let from_residual_expr = self.wrap_in_try_constructor( - hir::LangItem::TryTraitFromResidual, + if self.catch_scope.is_some() { + hir::LangItem::ResidualIntoTryType + } else { + hir::LangItem::TryTraitFromResidual + }, try_span, self.arena.alloc(residual_expr), unstable_span, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 9cb17ea67a377..1f36454ec861a 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -183,7 +183,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_contracts: [sym::contracts_internals].into(), - allow_try_trait: [sym::try_trait_v2, sym::yeet_desugar_details].into(), + allow_try_trait: [ + sym::try_trait_v2, + sym::try_trait_v2_residual, + sym::yeet_desugar_details, + ] + .into(), allow_pattern_type: [sym::pattern_types, sym::pattern_type_range_trait].into(), allow_gen_future: if tcx.features().async_fn_track_caller() { [sym::gen_future, sym::closure_track_caller].into() diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index ed0cf2716028b..0a6e491227d01 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -370,6 +370,7 @@ language_item_table! { TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None; TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None; + ResidualIntoTryType, sym::into_try_type, into_try_type_fn, Target::Fn, GenericRequirement::None; CoercePointeeValidated, sym::coerce_pointee_validated, coerce_pointee_validated_trait, Target::Trait, GenericRequirement::Exact(0); diff --git a/compiler/rustc_hir_typeck/src/inline_asm.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index c0cd23be6909d..6460bd72c7973 100644 --- a/compiler/rustc_hir_typeck/src/inline_asm.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -7,7 +7,7 @@ use rustc_middle::bug; use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_target::asm::{ InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, }; @@ -27,6 +27,7 @@ enum NonAsmTypeReason<'tcx> { InvalidElement(DefId, Ty<'tcx>), NotSizedPtr(Ty<'tcx>), EmptySIMDArray(Ty<'tcx>), + Tainted(ErrorGuaranteed), } impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { @@ -93,6 +94,14 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } } ty::Adt(adt, args) if adt.repr().simd() => { + if !adt.is_struct() { + let guar = self.fcx.dcx().span_delayed_bug( + span, + format!("repr(simd) should only be used on structs, got {}", adt.descr()), + ); + return Err(NonAsmTypeReason::Tainted(guar)); + } + let fields = &adt.non_enum_variant().fields; if fields.is_empty() { return Err(NonAsmTypeReason::EmptySIMDArray(ty)); @@ -234,6 +243,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = format!("use of empty SIMD vector `{ty}`"); self.fcx.dcx().struct_span_err(expr.span, msg).emit(); } + NonAsmTypeReason::Tainted(_error_guard) => { + // An error has already been reported. + } } return None; } diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index ade7ec38fb353..2e100a6215c09 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -252,7 +252,7 @@ pub(crate) fn run_in_thread_pool_with_globals< let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs().expect("failed to collect active queries in deadlock handler") + QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler") }); break_query_cycles(query_map, ®istry); }) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 4e601a6c59442..39b6fac4ebc0b 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -88,17 +88,25 @@ impl<'tcx> QueryContext for QueryCtxt<'tcx> { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a query map representing active query jobs. - /// It returns an incomplete map as an error if it fails - /// to take locks. + /// Returns a map of currently active query jobs. + /// + /// If `require_complete` is `true`, this function locks all shards of the + /// query results to produce a complete map, which always returns `Ok`. + /// Otherwise, it may return an incomplete map as an error if any shard + /// lock cannot be acquired. + /// + /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, + /// especially when called from within a deadlock handler, unless a + /// complete map is needed and no deadlock is possible at this call site. fn collect_active_jobs( self, + require_complete: bool, ) -> Result>, QueryMap>> { let mut jobs = QueryMap::default(); let mut complete = true; - for collect in super::TRY_COLLECT_ACTIVE_JOBS.iter() { - if collect(self.tcx, &mut jobs).is_none() { + for collect in super::COLLECT_ACTIVE_JOBS.iter() { + if collect(self.tcx, &mut jobs, require_complete).is_none() { complete = false; } } @@ -163,11 +171,7 @@ impl<'tcx> QueryContext for QueryCtxt<'tcx> { } fn depth_limit_error(self, job: QueryJobId) { - // FIXME: `collect_active_jobs` expects no locks to be held, which doesn't hold for this call. - let query_map = match self.collect_active_jobs() { - Ok(query_map) => query_map, - Err(query_map) => query_map, - }; + let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); let (info, depth) = job.find_dep_kind_root(query_map); let suggested_limit = match self.recursion_limit() { @@ -731,19 +735,21 @@ macro_rules! define_queries { } } - pub(crate) fn try_collect_active_jobs<'tcx>( + pub(crate) fn collect_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, qmap: &mut QueryMap>, + require_complete: bool, ) -> Option<()> { let make_query = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; - let res = tcx.query_system.states.$name.try_collect_active_jobs( + let res = tcx.query_system.states.$name.collect_active_jobs( tcx, make_query, qmap, + require_complete, ); // this can be called during unwinding, and the function has a `try_`-prefix, so // don't `unwrap()` here, just manually check for `None` and do best-effort error @@ -814,10 +820,10 @@ macro_rules! define_queries { // These arrays are used for iteration and can't be indexed by `DepKind`. - const TRY_COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>) -> Option<()> + const COLLECT_ACTIVE_JOBS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap>, bool) -> Option<()> ] = - &[$(query_impl::$name::try_collect_active_jobs),*]; + &[$(query_impl::$name::collect_active_jobs),*]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index fd1ea997ebe56..7d9b594d501ff 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -616,7 +616,7 @@ pub fn print_query_stack( let mut count_total = 0; // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs() { + let query_map = match qcx.collect_active_jobs(false) { Ok(query_map) => query_map, Err(query_map) => query_map, }; diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 855769dacc3e1..ce3456d532e69 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -161,7 +161,10 @@ pub trait QueryContext: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self) -> Result, QueryMap>; + fn collect_active_jobs( + self, + require_complete: bool, + ) -> Result, QueryMap>; fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index e74de5edc42d7..dea47c8fa787e 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,10 +7,12 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; +use hashbrown::HashTable; use hashbrown::hash_table::Entry; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; +use rustc_data_structures::sync::LockGuard; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; @@ -63,22 +65,33 @@ where self.active.lock_shards().all(|shard| shard.is_empty()) } - pub fn try_collect_active_jobs( + pub fn collect_active_jobs( &self, qcx: Qcx, make_query: fn(Qcx, K) -> QueryStackFrame, jobs: &mut QueryMap, + require_complete: bool, ) -> Option<()> { let mut active = Vec::new(); - // We use try_lock_shards here since we are called from the - // deadlock handler, and this shouldn't be locked. - for shard in self.active.try_lock_shards() { - for (k, v) in shard?.iter() { + let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult)>>| { + for (k, v) in iter.iter() { if let QueryResult::Started(ref job) = *v { - active.push((*k, (*job).clone())); + active.push((*k, job.clone())); } } + }; + + if require_complete { + for shard in self.active.lock_shards() { + collect(shard); + } + } else { + // We use try_lock_shards here since we are called from the + // deadlock handler, and this shouldn't be locked. + for shard in self.active.try_lock_shards() { + collect(shard?); + } } // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call @@ -271,7 +284,7 @@ where { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs().ok().expect("failed to collect active queries"); + let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift(qcx)), None) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a4d86e5df9a91..8ab8181833064 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1259,6 +1259,7 @@ symbols! { into_async_iter_into_iter, into_future, into_iter, + into_try_type, intra_doc_pointers, intrinsics, intrinsics_unaligned_volatile_load, @@ -2280,6 +2281,7 @@ symbols! { try_from_fn, try_into, try_trait_v2, + try_trait_v2_residual, try_update, tt, tuple, diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index e1f2ebcf4c289..f68782c804cdb 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -359,11 +359,24 @@ where /// and in the other direction, /// ` as Residual>::TryType = Result`. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] -#[rustc_const_unstable(feature = "const_try", issue = "74935")] -pub const trait Residual { +#[rustc_const_unstable(feature = "const_try_residual", issue = "91285")] +pub const trait Residual: Sized { /// The "return" type of this meta-function. #[unstable(feature = "try_trait_v2_residual", issue = "91285")] - type TryType: Try; + type TryType: [const] Try; +} + +/// Used in `try {}` blocks so the type produced in the `?` desugaring +/// depends on the residual type `R` and the output type of the block `O`, +/// but importantly not on the contextual type the way it would be if +/// we called `<_ as FromResidual>::from_residual(r)` directly. +#[unstable(feature = "try_trait_v2_residual", issue = "91285")] +// needs to be `pub` to avoid `private type` errors +#[expect(unreachable_pub)] +#[inline] // FIXME: force would be nice, but fails -- see #148915 +#[lang = "into_try_type"] +pub fn residual_into_try_type, O>(r: R) -> >::TryType { + FromResidual::from_residual(r) } #[unstable(feature = "pub_crate_should_not_need_unstable_attr", issue = "none")] diff --git a/library/std/src/sys/pal/unix/kernel_copy/tests.rs b/library/std/src/sys/pal/unix/kernel_copy/tests.rs index 54d8f8ed2edd4..15dee768d928b 100644 --- a/library/std/src/sys/pal/unix/kernel_copy/tests.rs +++ b/library/std/src/sys/pal/unix/kernel_copy/tests.rs @@ -50,7 +50,7 @@ fn copy_specialization() -> Result<()> { "inner Take allowed reading beyond end of file, some bytes should be left" ); - let mut sink = sink.into_inner()?; + let mut sink = sink.into_inner().map_err(io::Error::from)?; sink.seek(SeekFrom::Start(0))?; let mut copied = Vec::new(); sink.read_to_end(&mut copied)?; diff --git a/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-abort.mir b/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-abort.mir index 5b401064dd0e1..55f44d954d806 100644 --- a/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-abort.mir @@ -34,7 +34,7 @@ fn option_traits(_1: Option) -> Option { } bb3: { - _0 = as FromResidual>>::from_residual(const Option::::None) -> [return: bb4, unwind unreachable]; + _0 = ops::try_trait::residual_into_try_type::, u32>(const Option::::None) -> [return: bb4, unwind unreachable]; } bb4: { diff --git a/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-unwind.mir index bda9e9d8e6045..b42aea38d3e35 100644 --- a/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/option_bubble_debug.option_traits.PreCodegen.after.panic-unwind.mir @@ -34,7 +34,7 @@ fn option_traits(_1: Option) -> Option { } bb3: { - _0 = as FromResidual>>::from_residual(const Option::::None) -> [return: bb4, unwind continue]; + _0 = ops::try_trait::residual_into_try_type::, u32>(const Option::::None) -> [return: bb4, unwind continue]; } bb4: { diff --git a/tests/ui/asm/invalid-repr-simd-on-enum-148634.rs b/tests/ui/asm/invalid-repr-simd-on-enum-148634.rs new file mode 100644 index 0000000000000..45f1f092e5268 --- /dev/null +++ b/tests/ui/asm/invalid-repr-simd-on-enum-148634.rs @@ -0,0 +1,16 @@ +//@ needs-asm-support +#![feature(repr_simd)] + +use std::arch::asm; + +#[repr(simd)] +//~^ ERROR attribute should be applied to a struct +//~| ERROR unsupported representation for zero-variant enum +enum Es {} + +fn main() { + unsafe { + let mut x: Es; + asm!("{}", out(reg) x); + } +} diff --git a/tests/ui/asm/invalid-repr-simd-on-enum-148634.stderr b/tests/ui/asm/invalid-repr-simd-on-enum-148634.stderr new file mode 100644 index 0000000000000..6992d03f4fa68 --- /dev/null +++ b/tests/ui/asm/invalid-repr-simd-on-enum-148634.stderr @@ -0,0 +1,22 @@ +error[E0517]: attribute should be applied to a struct + --> $DIR/invalid-repr-simd-on-enum-148634.rs:6:8 + | +LL | #[repr(simd)] + | ^^^^ +... +LL | enum Es {} + | ---------- not a struct + +error[E0084]: unsupported representation for zero-variant enum + --> $DIR/invalid-repr-simd-on-enum-148634.rs:6:8 + | +LL | #[repr(simd)] + | ^^^^ +... +LL | enum Es {} + | ------- zero-variant enum + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0084, E0517. +For more information about an error, try `rustc --explain E0084`. diff --git a/tests/ui/try-block/try-block-bad-type.rs b/tests/ui/try-block/try-block-bad-type.rs index 00cd0af127c87..c302496b29b1c 100644 --- a/tests/ui/try-block/try-block-bad-type.rs +++ b/tests/ui/try-block/try-block-bad-type.rs @@ -4,7 +4,7 @@ pub fn main() { let res: Result = try { - Err("")?; //~ ERROR `?` couldn't convert the error + Err("")?; //~ ERROR mismatched types 5 }; diff --git a/tests/ui/try-block/try-block-bad-type.stderr b/tests/ui/try-block/try-block-bad-type.stderr index cf1cd60a7d5e4..9df01a4cf5b16 100644 --- a/tests/ui/try-block/try-block-bad-type.stderr +++ b/tests/ui/try-block/try-block-bad-type.stderr @@ -1,16 +1,15 @@ -error[E0277]: `?` couldn't convert the error to `TryFromSliceError` - --> $DIR/try-block-bad-type.rs:7:16 +error[E0308]: mismatched types + --> $DIR/try-block-bad-type.rs:7:9 | LL | Err("")?; - | -------^ the trait `From<&str>` is not implemented for `TryFromSliceError` - | | - | this can't be annotated with `?` because it has type `Result<_, &str>` - | - = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait -help: the trait `From<&str>` is not implemented for `TryFromSliceError` - but trait `From` is implemented for it - --> $SRC_DIR/core/src/array/mod.rs:LL:COL - = help: for that trait implementation, expected `Infallible`, found `&str` + | ^^^^^^^^ expected `Result`, found `Result<_, &str>` + | + = note: expected enum `Result` + found enum `Result<_, &str>` +help: consider using `Result::expect` to unwrap the `Result<_, &str>` value, panicking if the value is a `Result::Err` + | +LL | Err("")?.expect("REASON"); + | +++++++++++++++++ error[E0271]: type mismatch resolving ` as Try>::Output == &str` --> $DIR/try-block-bad-type.rs:12:9 @@ -42,5 +41,5 @@ LL | let res: i32 = try { 5 }; error: aborting due to 5 previous errors -Some errors have detailed explanations: E0271, E0277. +Some errors have detailed explanations: E0271, E0277, E0308. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/try-block/try-block-homogeneous.rs b/tests/ui/try-block/try-block-homogeneous.rs new file mode 100644 index 0000000000000..2135d56e3bda0 --- /dev/null +++ b/tests/ui/try-block/try-block-homogeneous.rs @@ -0,0 +1,36 @@ +//@ check-pass +//@ edition: 2018 + +#![feature(try_blocks)] +#![crate_type = "lib"] + +// A few examples of code that only works with homogeneous `try` + +pub fn index_or_zero(x: &[i32], i: usize, j: usize) -> i32 { + // With heterogeneous `try` this fails because + // it tries to call a method on a type variable. + try { x.get(i)? + x.get(j)? }.unwrap_or(0) +} + +pub fn do_nothing_on_errors(a: &str, b: &str) { + // With heterogeneous `try` this fails because + // an underscore pattern doesn't constrain the output type. + let _ = try { + let a = a.parse::()?; + let b = b.parse::()?; + println!("{a} {b}"); + }; +} + +pub fn print_error_once(a: &str, b: &str) { + match try { + let a = a.parse::()?; + let b = b.parse::()?; + (a, b) + } { + Ok(_pair) => {} + // With heterogeneous `try` this fails because + // nothing constrains the error type in `e`. + Err(e) => eprintln!("{e}"), + } +}