Skip to content

Commit 3d6be00

Browse files
Port call expr type checking and closure upvar inference from rustc
1 parent c6e5e8c commit 3d6be00

64 files changed

Lines changed: 5104 additions & 2126 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bench_data/glorious_old_parser

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//- minicore: fn
12
use crate::ast::{AngleBracketedArgs, ParenthesizedArgs, AttrStyle, BareFnTy};
23
use crate::ast::{GenericBound, TraitBoundModifier};
34
use crate::ast::Unsafety;

crates/hir-def/src/expr_store.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,13 @@ impl ExpressionStore {
929929
// We keep the async closure exactly one expr before.
930930
ExprId::from_raw(la_arena::RawIdx::from_u32(coroutine_closure.into_raw().into_u32() - 1))
931931
}
932+
933+
/// The opposite of [`Self::coroutine_for_closure()`].
934+
#[inline]
935+
pub fn closure_for_coroutine(coroutine: ExprId) -> ExprId {
936+
// We keep the async closure exactly one expr before.
937+
ExprId::from_raw(la_arena::RawIdx::from_u32(coroutine.into_raw().into_u32() + 1))
938+
}
932939
}
933940

934941
impl Index<ExprId> for ExpressionStore {

crates/hir-def/src/expr_store/lower.rs

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -945,12 +945,19 @@ impl<'db> ExprCollector<'db> {
945945
})
946946
}
947947

948-
/// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
949-
/// non-captured patterns such as wildcards (to ensure consistent drop order).
950-
fn lower_async_fn(&mut self, params: &mut Vec<PatId>, body: ExprId) -> ExprId {
948+
/// Lowers a desugared coroutine body after moving all of the arguments
949+
/// into the body. This is to make sure that the future actually owns the
950+
/// arguments that are passed to the function, and to ensure things like
951+
/// drop order are stable.
952+
fn lower_async_block_with_moved_arguments(
953+
&mut self,
954+
params: &mut [PatId],
955+
body: ExprId,
956+
coroutine_source: CoroutineSource,
957+
) -> ExprId {
951958
let mut statements = Vec::new();
952959
for param in params {
953-
let name = match self.store.pats[*param] {
960+
let (name, hygiene) = match self.store.pats[*param] {
954961
Pat::Bind { id, .. }
955962
if matches!(
956963
self.store.bindings[id].mode,
@@ -962,14 +969,16 @@ impl<'db> ExprCollector<'db> {
962969
}
963970
Pat::Bind { id, .. } => {
964971
// If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
965-
self.store.bindings[id].name.clone()
972+
(self.store.bindings[id].name.clone(), self.store.bindings[id].hygiene)
966973
}
967-
_ => self.generate_new_name(),
974+
_ => (self.generate_new_name(), HygieneId::ROOT),
968975
};
969-
let binding_id =
970-
self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
976+
let binding_id = self.alloc_binding(name.clone(), BindingAnnotation::Mutable, hygiene);
971977
let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
972978
let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
979+
if !hygiene.is_root() {
980+
self.store.ident_hygiene.insert(expr.into(), hygiene);
981+
}
973982
statements.push(Statement::Let {
974983
pat: *param,
975984
type_ref: None,
@@ -980,12 +989,17 @@ impl<'db> ExprCollector<'db> {
980989
}
981990

982991
let async_ = self.async_block(
983-
CoroutineSource::Fn,
984-
CaptureBy::Value,
992+
coroutine_source,
993+
// The default capture mode here is by-ref. Later on during upvar analysis,
994+
// we will force the captured arguments to by-move, but for async closures,
995+
// we want to make sure that we avoid unnecessarily moving captures, or else
996+
// all async closures would default to `FnOnce` as their calling mode.
997+
CaptureBy::Ref,
985998
None,
986999
statements.into_boxed_slice(),
9871000
Some(body),
9881001
);
1002+
// It's important that this comes last, see the lowering of async closures for why.
9891003
self.alloc_expr_desugared(async_)
9901004
}
9911005

@@ -1010,14 +1024,18 @@ impl<'db> ExprCollector<'db> {
10101024

10111025
fn collect(
10121026
&mut self,
1013-
params: &mut Vec<PatId>,
1027+
params: &mut [PatId],
10141028
expr: Option<ast::Expr>,
10151029
awaitable: Awaitable,
10161030
) -> ExprId {
10171031
self.awaitable_context.replace(awaitable);
10181032
self.with_label_rib(RibKind::Closure, |this| {
10191033
let body = this.collect_expr_opt(expr);
1020-
if awaitable == Awaitable::Yes { this.lower_async_fn(params, body) } else { body }
1034+
if awaitable == Awaitable::Yes {
1035+
this.lower_async_block_with_moved_arguments(params, body, CoroutineSource::Fn)
1036+
} else {
1037+
body
1038+
}
10211039
})
10221040
}
10231041

@@ -1450,18 +1468,11 @@ impl<'db> ExprCollector<'db> {
14501468
} else if e.async_token().is_some() {
14511469
// It's important that this expr is allocated immediately before the closure.
14521470
// We rely on it for `coroutine_for_closure()`.
1453-
body = this.alloc_expr_desugared(Expr::Closure {
1454-
args: Box::default(),
1455-
arg_types: Box::default(),
1456-
ret_type: None,
1471+
body = this.lower_async_block_with_moved_arguments(
1472+
&mut args,
14571473
body,
1458-
closure_kind: ClosureKind::AsyncBlock {
1459-
source: CoroutineSource::Closure,
1460-
},
1461-
// The block may need to capture by move, but we cannot know it now.
1462-
// It will be fixed in capture analysis.
1463-
capture_by: CaptureBy::Ref,
1464-
});
1474+
CoroutineSource::Closure,
1475+
);
14651476
body_is_bindings_owner = true;
14661477

14671478
ClosureKind::AsyncClosure

crates/hir-def/src/expr_store/tests/body.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ fn async_fn_weird_param_patterns() {
652652
async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {}
653653
"#,
654654
expect![[r#"
655-
fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async move {
655+
fn main(self, param1, mut param2, mut <ra@gennew>0, param4 @ _, mut <ra@gennew>1) async {
656656
let ref mut param2 = param2;
657657
let _ = <ra@gennew>0;
658658
let 123 = <ra@gennew>1;

crates/hir-def/src/lang_item.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,7 @@ language_item_table! { LangItems =>
306306
/// Trait injected by `#[derive(Eq)]`, (i.e. "Total EQ"; no, I will not apologize).
307307
StructuralTeq, sym::structural_teq, TraitId;
308308
Copy, sym::copy, TraitId;
309+
UseCloned, sym::use_cloned, TraitId;
309310
Clone, sym::clone, TraitId;
310311
TrivialClone, sym::trivial_clone, TraitId;
311312
Sync, sym::sync, TraitId;

crates/hir-ty/src/consteval/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1792,14 +1792,14 @@ const GOAL: i32 = {
17921792
fn closure_capture_unsized_type() {
17931793
check_number(
17941794
r#"
1795-
//- minicore: fn, copy, slice, index, coerce_unsized
1795+
//- minicore: fn, copy, slice, index, coerce_unsized, sized
17961796
fn f<T: A>(x: &<T as A>::Ty) -> &<T as A>::Ty {
17971797
let c = || &*x;
17981798
c()
17991799
}
18001800
18011801
trait A {
1802-
type Ty;
1802+
type Ty: ?Sized;
18031803
}
18041804
18051805
impl A for i32 {
@@ -1810,7 +1810,7 @@ fn closure_capture_unsized_type() {
18101810
let k: &[u8] = &[1, 2, 3];
18111811
let k = f::<i32>(k);
18121812
k[0] + k[1] + k[2]
1813-
}
1813+
};
18141814
"#,
18151815
6,
18161816
);

crates/hir-ty/src/diagnostics/expr.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,7 @@ impl<'db> ExprValidator<'db> {
238238
if (pat_ty == scrut_ty
239239
|| scrut_ty
240240
.as_reference()
241-
.map(|(match_expr_ty, ..)| match_expr_ty == pat_ty)
242-
.unwrap_or(false))
241+
.is_none_or(|(match_expr_ty, ..)| match_expr_ty == pat_ty))
243242
&& types_of_subpatterns_do_match(arm.pat, self.body, self.infer)
244243
{
245244
// If we had a NotUsefulMatchArm diagnostic, we could

crates/hir-ty/src/diagnostics/match_check/pat_util.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ pub(crate) trait EnumerateAndAdjustIterator {
3838
}
3939

4040
impl<T: ExactSizeIterator> EnumerateAndAdjustIterator for T {
41+
/// When there is a list of items with a gap of an unknown length inside, and another list
42+
/// of item it should be zipped against, this operates on the list with the gap and returns,
43+
/// for each item, the index it should match in the other list.
44+
///
45+
/// When compiling Rust, such situation often occurs for tuple structs/tuples with a rest pattern
46+
/// that should be matched against the fields.
4147
fn enumerate_and_adjust(
4248
self,
4349
expected_len: usize,

crates/hir-ty/src/display.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use span::Edition;
4747
use stdx::never;
4848

4949
use crate::{
50-
CallableDefId, FnAbi, ImplTraitId, InferenceResult, MemoryMap, ParamEnvAndCrate, consteval,
50+
CallableDefId, FnAbi, ImplTraitId, MemoryMap, ParamEnvAndCrate, consteval,
5151
db::{HirDatabase, InternedClosure},
5252
generics::generics,
5353
layout::Layout,
@@ -1495,9 +1495,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
14951495
}
14961496
let sig = interner.signature_unclosure(substs.as_closure().sig(), Safety::Safe);
14971497
let sig = sig.skip_binder();
1498-
let InternedClosure(owner, _) = id.loc(db);
1499-
let infer = InferenceResult::of(db, owner);
1500-
let (_, kind) = infer.closure_info(id);
1498+
let kind = substs.as_closure().kind();
15011499
match f.closure_style {
15021500
ClosureStyle::ImplFn => write!(f, "impl {kind:?}(")?,
15031501
ClosureStyle::RANotation => write!(f, "|")?,

0 commit comments

Comments
 (0)