Skip to content

Commit

Permalink
Merge #197
Browse files Browse the repository at this point in the history
197: API, Rustc: Add `AwaitExpr` and support `async` blocks and fns r=xFrednet a=xFrednet

Basically, what the title says :D

---

Async and await stuff in rustc heavily relies on syntax sugar. A simple `.await` call is transformed into one loop and two match statements etc. This far Marker has always *resugared* this syntax, for two reasons:
1. I believe that the exact desugar of expressions is allowed to change.
2. The desugar is often harder to understand.

In this case, I've decided to also resugar the expressions. For `.await` expressions, I'm sure that this is the right call. However, async blocks and functions might be different. For example:

```rust
async fn foo() -> u8 {
	16
}
```

The function actually has the return type `impl Future<Output = u8>` and not `u8` like the AST node currently says. The `impl Future<Output = u8>` can still be retrieved as the semantic type of the expression.

The basic question I currently have is: Is resugaring the output of the function to `u8` okay, or should it still say `impl Future<Output = u8>` even if the user didn't write it? Personally, I like this resugar, but I haven't worked too much with async.

---

`@Veetaha` you said previously that you work with async. Do you maybe have any thoughts on this? (I hope it's okay that I ping you, if not, please tell me, and I will refrain from it in the future)

---

Closes #174

Co-authored-by: xFrednet <xFrednet@gmail.com>
  • Loading branch information
bors[bot] and xFrednet authored Aug 3, 2023
2 parents 531a51c + b6e19c9 commit 2bf48e1
Show file tree
Hide file tree
Showing 26 changed files with 1,323 additions and 144 deletions.
5 changes: 4 additions & 1 deletion marker_api/src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ pub enum ExprKind<'ast> {
For(&'ast ForExpr<'ast>),
Loop(&'ast LoopExpr<'ast>),
While(&'ast WhileExpr<'ast>),
Await(&'ast AwaitExpr<'ast>),
Unstable(&'ast UnstableExpr<'ast>),
}

Expand Down Expand Up @@ -163,6 +164,7 @@ pub enum ExprPrecedence {
For = 0x1400_0004,
Loop = 0x1400_0005,
While = 0x1400_0006,
Await = 0x1400_0007,

Path = 0x1300_0000,

Expand Down Expand Up @@ -252,6 +254,7 @@ macro_rules! impl_expr_kind_fn {
Call, Method,
Array, Tuple, Ctor, Range,
If, Let, Match, Break, Return, Continue, For, Loop, While,
Await,
Unstable
);
};
Expand Down Expand Up @@ -376,7 +379,7 @@ mod test {
assert_eq!(48, size_of::<StrLitExpr<'_>>(), "StrLitExpr<'_>");
assert_eq!(24, size_of::<CharLitExpr<'_>>(), "CharLitExpr<'_>");
assert_eq!(24, size_of::<BoolLitExpr<'_>>(), "BoolLitExpr<'_>");
assert_eq!(88, size_of::<BlockExpr<'_>>(), "BlockExpr<'_>");
assert_eq!(96, size_of::<BlockExpr<'_>>(), "BlockExpr<'_>");
assert_eq!(72, size_of::<ClosureExpr<'_>>(), "ClosureExpr<'_>");
assert_eq!(40, size_of::<UnaryOpExpr<'_>>(), "UnaryOpExpr<'_>");
assert_eq!(40, size_of::<RefExpr<'_>>(), "RefExpr<'_>");
Expand Down
47 changes: 41 additions & 6 deletions marker_api/src/ast/expr/block_expr.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
ast::{pat::PatKind, stmt::StmtKind, ty::SynTyKind, BodyId, Ident, Span, SpanId},
ast::{pat::PatKind, stmt::StmtKind, ty::SynTyKind, BodyId, Ident, Safety, Span, SpanId, Syncness},
context::with_cx,
ffi::{FfiOption, FfiSlice},
};
Expand All @@ -23,6 +23,9 @@ use super::{CommonExprData, ExprKind};
///
/// // vvvvvv An optional label to be targeted by break expressions
/// let _ = 'label: {
/// let _ = 18;
/// // ^^^^^^^^^^^
/// // A statement in the block
/// 12
/// };
/// ```
Expand All @@ -31,14 +34,19 @@ use super::{CommonExprData, ExprKind};
/// expression at the end of a block is called *block expression*. The meaning
/// depends on the context. Marker's documentation will try to make the meaning
/// clear by linking directly to the [`BlockExpr`] struct or calling it a *block*.
///
/// This expression also represents async blocks, the internal desugar used by
/// rustc is resugared for this.
#[repr(C)]
#[derive(Debug)]
pub struct BlockExpr<'ast> {
data: CommonExprData<'ast>,
stmts: FfiSlice<'ast, StmtKind<'ast>>,
expr: FfiOption<ExprKind<'ast>>,
label: FfiOption<Ident<'ast>>,
is_unsafe: bool,
safety: Safety,
syncness: Syncness,
capture_kind: CaptureKind,
}

impl<'ast> BlockExpr<'ast> {
Expand All @@ -58,8 +66,31 @@ impl<'ast> BlockExpr<'ast> {
self.label.get()
}

pub fn is_unsafe(&self) -> bool {
self.is_unsafe
pub fn safety(&self) -> Safety {
self.safety
}

pub fn syncness(&self) -> Syncness {
self.syncness
}

/// The capture kind of this block. For normal blocks, this will always be
/// [`CaptureKind::Default`], which in this context means no capture at all.
/// Async blocks are special, as they can capture values by move, indicated
/// by the `move` keyword, like in this:
///
/// ```
/// # use std::future::Future;
/// # fn foo<'a>(x: &'a u8) -> impl Future<Output = u8> + 'a {
/// // The whole block expression
/// // vvvvvvvvvvvvvvvvv
/// async move { *x }
/// // ^^^^
/// // The move keyword defining the capture kind
/// # }
/// ```
pub fn capture_kind(&self) -> CaptureKind {
self.capture_kind
}
}

Expand All @@ -72,14 +103,18 @@ impl<'ast> BlockExpr<'ast> {
stmts: &'ast [StmtKind<'ast>],
expr: Option<ExprKind<'ast>>,
label: Option<Ident<'ast>>,
is_unsafe: bool,
safety: Safety,
syncness: Syncness,
capture_kind: CaptureKind,
) -> Self {
Self {
data,
stmts: stmts.into(),
expr: expr.into(),
label: label.into(),
is_unsafe,
safety,
syncness,
capture_kind,
}
}
}
Expand Down
41 changes: 41 additions & 0 deletions marker_api/src/ast/expr/op_exprs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,44 @@ impl<'ast> AssignExpr<'ast> {
}
}
}

/// An `.await` expression on a future, like:
///
/// ```
/// # async fn foo() -> u8 {
/// # 16
/// # }
/// # async fn wrapper() {
/// // The await expression
/// // vvvvvvvvvvv
/// foo().await;
/// // ^^^^^
/// // The future, that will be awaited
/// # }
/// ```
///
/// Marker specificity hides the desugar of `.await` expressions. The [Rust Reference]
/// contains more information how rustc desugars `.await` expressions.
///
/// [Rust Reference]: <https://doc.rust-lang.org/reference/expressions/await-expr.html>
#[repr(C)]
#[derive(Debug)]
pub struct AwaitExpr<'ast> {
data: CommonExprData<'ast>,
expr: ExprKind<'ast>,
}

impl<'ast> AwaitExpr<'ast> {
pub fn expr(&self) -> ExprKind<'ast> {
self.expr
}
}

super::impl_expr_data!(AwaitExpr<'ast>, Await);

#[cfg(feature = "driver-api")]
impl<'ast> AwaitExpr<'ast> {
pub fn new(data: CommonExprData<'ast>, expr: ExprKind<'ast>) -> Self {
Self { data, expr }
}
}
12 changes: 12 additions & 0 deletions marker_api/src/ast/item/fn_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,20 @@ use super::CommonItemData;
/// // A function without a body
/// fn baz(_: i32);
/// }
///
/// // An async function.
/// async fn foo_async() -> u8 {
/// // ...
/// # 16
/// }
/// ```
///
/// Async functions in Rustc actually return a future with the defined output type.
/// The return type `-> u8` gets desugared to `impl Future<Output = u8>`. Marker will
/// resugar the type to what the user had originally written. In this case it would
/// just return `u8`. The semantic return type of an async function can be retrieved
/// from the expression of the [`Body`][super::Body].
///
/// See: <https://doc.rust-lang.org/reference/items/functions.html>
#[repr(C)]
#[derive(Debug)]
Expand Down
4 changes: 4 additions & 0 deletions marker_api/src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ pub use crate::ast::ty::SynTyData;
// Common types
pub use crate::ast::expr::ExprKind;
pub use crate::ast::item::ItemKind;
pub use crate::ast::pat::PatKind;
pub use crate::ast::stmt::StmtKind;
pub use crate::ast::ty::SemTyKind;
pub use crate::ast::ty::SynTyKind;
pub use crate::ast::Ident;
pub use crate::ast::Span;
pub use crate::AstContext;
37 changes: 20 additions & 17 deletions marker_rustc_driver/src/conversion/marker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,26 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> {
{
self.storage.alloc_slice(iter)
}

pub fn with_body<U, F>(&self, rustc_body_id: hir::BodyId, f: F) -> U
where
F: FnOnce() -> U,
{
// Body-Translation-Stack push
let prev_rustc_body_id = self.rustc_body.replace(Some(rustc_body_id));
let prev_rustc_ty_check = self.rustc_ty_check.take();
self.fill_rustc_ty_check();

// Operation
let res = f();

// Body-Translation-Stack pop
self.rustc_body.replace(prev_rustc_body_id);
self.rustc_ty_check.replace(prev_rustc_ty_check);

// Return result
res
}
}

impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> {
Expand All @@ -219,20 +239,3 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> {
))
}
}

macro_rules! with_body {
($cx:expr, $id:expr, $with:expr) => {
// Body-Translation-Stack push
let prev_rustc_body_id = $cx.rustc_body.replace(Some($id));
let prev_rustc_ty_check = $cx.rustc_ty_check.take();
$cx.fill_rustc_ty_check();

// Operation
$with

// Body-Translation-Stack pop
$cx.rustc_body.replace(prev_rustc_body_id);
$cx.rustc_ty_check.replace(prev_rustc_ty_check);
};
}
use with_body;
Loading

0 comments on commit 2bf48e1

Please sign in to comment.