Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1806,8 +1806,9 @@ pub enum ExprKind {
/// A use expression (`x.use`). Span is of use keyword.
Use(Box<Expr>, Span),

/// A try block (`try { ... }`).
TryBlock(Box<Block>),
/// A try block (`try { ... }`), if the type is `None`, or
/// A try block (`try bikeshed Ty { ... }`) if the type is `Some`.
TryBlock(Box<Block>, Option<Box<Ty>>),

/// An assignment (`a = foo()`).
/// The `Span` argument is the span of the `=` token.
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,8 +1048,8 @@ macro_rules! common_visitor_and_walkers {
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) =>
Expand Down
74 changes: 53 additions & 21 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::mem;
use std::ops::ControlFlow;
use std::sync::Arc;

Expand Down Expand Up @@ -27,7 +28,9 @@ use super::{
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 {}

Expand Down Expand Up @@ -199,7 +202,9 @@ impl<'hir> LoweringContext<'_, '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))),
Expand Down Expand Up @@ -562,9 +567,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::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
Expand Down Expand Up @@ -598,8 +608,16 @@ impl<'hir> LoweringContext<'_, '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(
Expand Down Expand Up @@ -1617,10 +1635,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
}

fn with_catch_scope<T>(&mut self, catch_id: hir::HirId, f: impl FnOnce(&mut Self) -> T) -> T {
let old_scope = self.catch_scope.replace(catch_id);
fn with_try_block_scope<T>(
&mut self,
scope: TryBlockScope,
f: impl FnOnce(&mut Self) -> T,
) -> T {
let old_scope = mem::replace(&mut self.try_block_scope, scope);
let result = f(self);
self.catch_scope = old_scope;
self.try_block_scope = old_scope;
result
}

Expand Down Expand Up @@ -1978,18 +2000,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
let residual_ident = Ident::with_dummy_span(sym::residual);
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 (constructor_item, target_id) = match self.try_block_scope {
TryBlockScope::Function => {
(hir::LangItem::TryTraitFromResidual, Err(hir::LoopIdError::OutsideLoopScope))
}
TryBlockScope::Homogeneous(block_id) => {
(hir::LangItem::ResidualIntoTryType, Ok(block_id))
}
TryBlockScope::Heterogeneous(block_id) => {
(hir::LangItem::TryTraitFromResidual, Ok(block_id))
}
};
let from_residual_expr = self.wrap_in_try_constructor(
if self.catch_scope.is_some() {
hir::LangItem::ResidualIntoTryType
} else {
hir::LangItem::TryTraitFromResidual
},
constructor_item,
try_span,
self.arena.alloc(residual_expr),
unstable_span,
);
let ret_expr = if let Some(catch_id) = self.catch_scope {
let target_id = Ok(catch_id);
let ret_expr = if target_id.is_ok() {
self.arena.alloc(self.expr(
try_span,
hir::ExprKind::Break(
Expand Down Expand Up @@ -2044,11 +2073,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
yeeted_span,
);

if let Some(catch_id) = self.catch_scope {
let target_id = Ok(catch_id);
hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
} else {
self.checked_return(Some(from_yeet_expr))
match self.try_block_scope {
TryBlockScope::Homogeneous(block_id) | TryBlockScope::Heterogeneous(block_id) => {
hir::ExprKind::Break(
hir::Destination { label: None, target_id: Ok(block_id) },
Some(from_yeet_expr),
)
}
TryBlockScope::Function => self.checked_return(Some(from_yeet_expr)),
}
}

Expand Down
17 changes: 13 additions & 4 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#![feature(if_let_guard)]
// tidy-alphabetical-end

use std::mem;
use std::sync::Arc;

use rustc_ast::node_id::NodeMap;
Expand Down Expand Up @@ -117,7 +118,7 @@ struct LoweringContext<'a, 'hir> {
/// outside of an `async fn`.
current_item: Option<Span>,

catch_scope: Option<HirId>,
try_block_scope: TryBlockScope,
loop_scope: Option<HirId>,
is_in_loop_condition: bool,
is_in_dyn_type: bool,
Expand Down Expand Up @@ -173,7 +174,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
trait_map: Default::default(),

// Lowering state.
catch_scope: None,
try_block_scope: TryBlockScope::Function,
loop_scope: None,
is_in_loop_condition: false,
is_in_dyn_type: false,
Expand Down Expand Up @@ -416,6 +417,14 @@ enum AstOwner<'a> {
ForeignItem(&'a ast::ForeignItem),
}

#[derive(Copy, Clone, Debug)]
enum TryBlockScope {
/// There isn't a `try` block, so the scope is the function or closure or ...
Function,
Homogeneous(HirId),
Heterogeneous(HirId),
}

fn index_crate<'a>(
node_id_to_def_id: &NodeMap<LocalDefId>,
krate: &'a Crate,
Expand Down Expand Up @@ -936,10 +945,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {

let old_contract = self.contract_ensures.take();

let catch_scope = self.catch_scope.take();
let try_block_scope = mem::replace(&mut self.try_block_scope, TryBlockScope::Function);
let loop_scope = self.loop_scope.take();
let ret = f(self);
self.catch_scope = catch_scope;
self.try_block_scope = try_block_scope;
self.loop_scope = loop_scope;

self.contract_ensures = old_contract;
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,9 +339,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {

fn visit_expr(&mut self, e: &'a ast::Expr) {
match e.kind {
ast::ExprKind::TryBlock(_) => {
ast::ExprKind::TryBlock(_, None) => {
gate!(&self, try_blocks, e.span, "`try` expression is experimental");
}
ast::ExprKind::TryBlock(_, Some(_)) => {
gate!(
&self,
try_blocks_heterogeneous,
e.span,
"`try bikeshed` expression is experimental"
);
}
ast::ExprKind::Lit(token::Lit {
kind: token::LitKind::Float | token::LitKind::Integer,
suffix,
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_ast_pretty/src/pprust/state/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,10 +818,15 @@ impl<'a> State<'a> {
);
self.word("?")
}
ast::ExprKind::TryBlock(blk) => {
ast::ExprKind::TryBlock(blk, opt_ty) => {
let cb = self.cbox(0);
let ib = self.ibox(0);
self.word_nbsp("try");
if let Some(ty) = opt_ty {
self.word_nbsp("bikeshed");
self.print_type(ty);
self.space();
}
self.print_block_with_attrs(blk, attrs, cb, ib)
}
ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => {
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/assert/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
| ExprKind::Path(_, _)
| ExprKind::Ret(_)
| ExprKind::Try(_)
| ExprKind::TryBlock(_)
| ExprKind::TryBlock(_, _)
| ExprKind::Type(_, _)
| ExprKind::Underscore
| ExprKind::While(_, _, _)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ declare_features! (
(unstable, trivial_bounds, "1.28.0", Some(48214)),
/// Allows using `try {...}` expressions.
(unstable, try_blocks, "1.29.0", Some(31436)),
/// Allows using `try bikeshed TargetType {...}` expressions.
(unstable, try_blocks_heterogeneous, "CURRENT_RUSTC_VERSION", Some(149488)),
/// Allows `impl Trait` to be used inside type aliases (RFC 2515).
(unstable, type_alias_impl_trait, "1.38.0", Some(63063)),
/// Allows creation of instances of a struct by moving fields that have
Expand Down
19 changes: 14 additions & 5 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3544,15 +3544,20 @@ impl<'a> Parser<'a> {
self.token.is_keyword(kw::Builtin) && self.look_ahead(1, |t| *t == token::Pound)
}

/// Parses a `try {...}` expression (`try` token already eaten).
/// Parses a `try {...}` or `try bikeshed Ty {...}` expression (`try` token already eaten).
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, Box<Expr>> {
let annotation =
if self.eat_keyword(exp!(Bikeshed)) { Some(self.parse_ty()?) } else { None };

let (attrs, body) = self.parse_inner_attrs_and_block(None)?;
if self.eat_keyword(exp!(Catch)) {
Err(self.dcx().create_err(errors::CatchAfterTry { span: self.prev_token.span }))
} else {
let span = span_lo.to(body.span);
self.psess.gated_spans.gate(sym::try_blocks, span);
Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body), attrs))
let gate_sym =
if annotation.is_none() { sym::try_blocks } else { sym::try_blocks_heterogeneous };
self.psess.gated_spans.gate(gate_sym, span);
Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body, annotation), attrs))
}
}

Expand All @@ -3569,7 +3574,11 @@ impl<'a> Parser<'a> {

fn is_try_block(&self) -> bool {
self.token.is_keyword(kw::Try)
&& self.look_ahead(1, |t| *t == token::OpenBrace || t.is_metavar_block())
&& self.look_ahead(1, |t| {
*t == token::OpenBrace
|| t.is_metavar_block()
|| t.kind == TokenKind::Ident(sym::bikeshed, IdentIsRaw::No)
})
&& self.token_uninterpolated_span().at_least_rust_2018()
}

Expand Down Expand Up @@ -4264,7 +4273,7 @@ impl MutVisitor for CondChecker<'_> {
| ExprKind::Closure(_)
| ExprKind::Block(_, _)
| ExprKind::Gen(_, _, _, _)
| ExprKind::TryBlock(_)
| ExprKind::TryBlock(_, _)
| ExprKind::Underscore
| ExprKind::Path(_, _)
| ExprKind::Break(_, _)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_parse/src/parser/token_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub enum TokenType {
// Keyword-like symbols.
// tidy-alphabetical-start
SymAttSyntax,
SymBikeshed,
SymClobberAbi,
SymInlateout,
SymInout,
Expand Down Expand Up @@ -556,6 +557,7 @@ macro_rules! exp {
(Yield) => { exp!(@kw, Yield, KwYield) };

(AttSyntax) => { exp!(@sym, att_syntax, SymAttSyntax) };
(Bikeshed) => { exp!(@sym, bikeshed, SymBikeshed) };
(ClobberAbi) => { exp!(@sym, clobber_abi, SymClobberAbi) };
(Inlateout) => { exp!(@sym, inlateout, SymInlateout) };
(Inout) => { exp!(@sym, inout, SymInout) };
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ symbols! {
begin_panic,
bench,
bevy_ecs,
bikeshed,
bikeshed_guaranteed_no_drop,
bin,
binaryheap_iter,
Expand Down Expand Up @@ -2277,6 +2278,7 @@ symbols! {
truncf64,
truncf128,
try_blocks,
try_blocks_heterogeneous,
try_capture,
try_from,
try_from_fn,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ fn ident_difference_expr_with_base_location(
| (Field(_, _), Field(_, _))
| (AssignOp(_, _, _), AssignOp(_, _, _))
| (Assign(_, _, _), Assign(_, _, _))
| (TryBlock(_), TryBlock(_))
| (TryBlock(_, _), TryBlock(_, _))
| (Await(_, _), Await(_, _))
| (Gen(_, _, _, _), Gen(_, _, _, _))
| (Block(_, _), Block(_, _))
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
) => eq_label(ll.as_ref(), rl.as_ref()) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt) && lk == rk,
(Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt),
(Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb),
(TryBlock(l), TryBlock(r)) => eq_block(l, r),
(TryBlock(lb, lt), TryBlock(rb, rt)) => eq_block(lb, rb) && both(lt.as_deref(), rt.as_deref(), eq_ty),
(Yield(l), Yield(r)) => eq_expr_opt(l.expr().map(Box::as_ref), r.expr().map(Box::as_ref)) && l.same_kind(r),
(Ret(l), Ret(r)) => eq_expr_opt(l.as_deref(), r.as_deref()),
(Break(ll, le), Break(rl, re)) => {
Expand Down
5 changes: 4 additions & 1 deletion src/tools/rustfmt/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,12 +363,13 @@ pub(crate) fn format_expr(
// Style Guide RFC for InlineAsm variant pending
// https://github.com/rust-dev-tools/fmt-rfcs/issues/152
ast::ExprKind::InlineAsm(..) => Ok(context.snippet(expr.span).to_owned()),
ast::ExprKind::TryBlock(ref block) => {
ast::ExprKind::TryBlock(ref block, None) => {
if let rw @ Ok(_) =
rewrite_single_line_block(context, "try ", block, Some(&expr.attrs), None, shape)
{
rw
} else {
// FIXME: 9 sounds like `"do catch ".len()`, so may predate the rename
// 9 = `try `
let budget = shape.width.saturating_sub(9);
Ok(format!(
Expand All @@ -384,6 +385,8 @@ pub(crate) fn format_expr(
))
}
}
// FIXME: heterogeneous try blocks, which include a type so are harder to format
ast::ExprKind::TryBlock(_, Some(_)) => Err(RewriteError::Unknown),
ast::ExprKind::Gen(capture_by, ref block, ref kind, _) => {
let mover = if matches!(capture_by, ast::CaptureBy::Value { .. }) {
"move "
Expand Down
6 changes: 6 additions & 0 deletions tests/pretty/try-blocks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//@ pp-exact
//@ edition: 2024

#![feature(try_blocks, try_blocks_heterogeneous)]

fn main() { try { Some(1)? }; try bikeshed Result<u32, ()> { 3 }; }
Loading
Loading