diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 2be2fca87c3c5..de2b8505d5c40 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1923,7 +1923,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), /// } @@ -1973,7 +1973,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 e454ed58699c9..391fba8395ce2 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -372,6 +372,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_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b6d1ff2432d6b..b3428fa044ada 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1260,6 +1260,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/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}"), + } +}