From 4033d19b79244b347a87f3c58d0ad01962935360 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 30 Nov 2025 14:46:36 -0800 Subject: [PATCH] Experimentally add *heterogeneous* `try` blocks 148725 moved the default to being homogeneous; this adds heterogeneous ones back under an obvious-bikeshed syntax so people can experiment with that as well. Essentially resolves 149025 by letting them move to this syntax instead. --- compiler/rustc_ast/src/ast.rs | 10 ++- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/expr.rs | 74 +++++++++++++------ compiler/rustc_ast_lowering/src/lib.rs | 21 +++++- compiler/rustc_ast_passes/src/feature_gate.rs | 10 ++- .../rustc_ast_pretty/src/pprust/state/expr.rs | 7 +- .../src/assert/context.rs | 2 +- compiler/rustc_feature/src/unstable.rs | 2 + compiler/rustc_parse/src/parser/expr.rs | 19 +++-- compiler/rustc_parse/src/parser/token_type.rs | 2 + compiler/rustc_span/src/symbol.rs | 2 + tests/pretty/try-blocks.rs | 6 ++ .../feature-gate-try_blocks_heterogeneous.rs | 9 +++ ...ature-gate-try_blocks_heterogeneous.stderr | 17 +++++ .../try-block-bad-type-heterogeneous.rs | 21 ++++++ .../try-block-bad-type-heterogeneous.stderr | 46 ++++++++++++ tests/ui/try-block/try-block-heterogeneous.rs | 19 +++++ tests/ui/unpretty/exhaustive.expanded.stdout | 8 +- tests/ui/unpretty/exhaustive.hir.stderr | 40 +++++----- tests/ui/unpretty/exhaustive.hir.stdout | 15 +++- tests/ui/unpretty/exhaustive.rs | 3 + 21 files changed, 278 insertions(+), 59 deletions(-) create mode 100644 tests/pretty/try-blocks.rs create mode 100644 tests/ui/feature-gates/feature-gate-try_blocks_heterogeneous.rs create mode 100644 tests/ui/feature-gates/feature-gate-try_blocks_heterogeneous.stderr create mode 100644 tests/ui/try-block/try-block-bad-type-heterogeneous.rs create mode 100644 tests/ui/try-block/try-block-bad-type-heterogeneous.stderr create mode 100644 tests/ui/try-block/try-block-heterogeneous.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index db7cace49ae8..09dd5c389632 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1806,8 +1806,14 @@ pub enum ExprKind { /// A use expression (`x.use`). Span is of use keyword. Use(Box, Span), - /// A try block (`try { ... }`). - TryBlock(Box), + /// A try block (`try { ... }`), if the type is `None`, or + /// A try block (`try bikeshed Ty { ... }`) if the type is `Some`. + /// + /// Note that `try bikeshed` is a *deliberately ridiculous* placeholder + /// syntax to avoid deciding what keyword or symbol should go there. + /// It's that way for experimentation only; an RFC to decide the final + /// semantics and syntax would be needed to put it on stabilization-track. + TryBlock(Box, Option>), /// An assignment (`a = foo()`). /// The `Span` argument is the span of the `=` token. diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index dde773fd147d..7a0424d39575 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -1048,8 +1048,8 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)? visit_visitable!($($mut)? vis, kind), ExprKind::Try(subexpression) => visit_visitable!($($mut)? vis, subexpression), - ExprKind::TryBlock(body) => - visit_visitable!($($mut)? vis, body), + ExprKind::TryBlock(body, optional_type) => + visit_visitable!($($mut)? vis, body, optional_type), ExprKind::Lit(token) => visit_visitable!($($mut)? vis, token), ExprKind::IncludedBytes(bytes) => diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 524f8b054cb4..7230e1c42474 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1,3 +1,4 @@ +use std::mem; use std::ops::ControlFlow; use std::sync::Arc; @@ -27,7 +28,9 @@ GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt, }; use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure}; -use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, fluent_generated}; +use crate::{ + AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope, fluent_generated, +}; struct WillCreateDefIdsVisitor {} @@ -199,7 +202,9 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> { ) }) } - ExprKind::TryBlock(body) => self.lower_expr_try_block(body), + ExprKind::TryBlock(body, opt_ty) => { + self.lower_expr_try_block(body, opt_ty.as_deref()) + } ExprKind::Match(expr, arms, kind) => hir::ExprKind::Match( self.lower_expr(expr), self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))), @@ -562,9 +567,14 @@ fn lower_expr_while_in_loop_scope( /// Desugar `try { ; }` into `{ ; ::std::ops::Try::from_output() }`, /// `try { ; }` into `{ ; ::std::ops::Try::from_output(()) }` /// and save the block id to use it as a break target for desugaring of the `?` operator. - fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> { + fn lower_expr_try_block(&mut self, body: &Block, opt_ty: Option<&Ty>) -> hir::ExprKind<'hir> { let body_hir_id = self.lower_node_id(body.id); - self.with_catch_scope(body_hir_id, |this| { + let new_scope = if opt_ty.is_some() { + TryBlockScope::Heterogeneous(body_hir_id) + } else { + TryBlockScope::Homogeneous(body_hir_id) + }; + let whole_block = self.with_try_block_scope(new_scope, |this| { let mut block = this.lower_block_noalloc(body_hir_id, body, true); // Final expression of the block (if present) or `()` with span at the end of block @@ -598,8 +608,16 @@ fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> { ok_wrapped_span, )); - hir::ExprKind::Block(this.arena.alloc(block), None) - }) + this.arena.alloc(block) + }); + + if let Some(ty) = opt_ty { + let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)); + let block_expr = self.arena.alloc(self.expr_block(whole_block)); + hir::ExprKind::Type(block_expr, ty) + } else { + hir::ExprKind::Block(whole_block, None) + } } fn wrap_in_try_constructor( @@ -1617,10 +1635,14 @@ fn lower_jump_destination(&mut self, id: NodeId, opt_label: Option