Skip to content

Commit 1a630df

Browse files
Do not gather local all together at the beginning of typeck
1 parent 0eb0b8c commit 1a630df

24 files changed

+292
-277
lines changed

compiler/rustc_hir_typeck/src/_match.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_trait_selection::traits::{
1111
use tracing::{debug, instrument};
1212

1313
use crate::coercion::{AsCoercionSite, CoerceMany};
14-
use crate::{Diverges, Expectation, FnCtxt, Needs};
14+
use crate::{Diverges, Expectation, FnCtxt, GatherLocalsVisitor, Needs};
1515

1616
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1717
#[instrument(skip(self), level = "debug", ret)]
@@ -43,6 +43,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4343
// #55810: Type check patterns first so we get types for all bindings.
4444
let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span);
4545
for arm in arms {
46+
GatherLocalsVisitor::gather_from_arm(self, arm);
47+
4648
self.check_pat_top(arm.pat, scrutinee_ty, Some(scrut_span), Some(scrut), None);
4749
}
4850

compiler/rustc_hir_typeck/src/check.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use std::cell::RefCell;
33
use rustc_abi::ExternAbi;
44
use rustc_hir as hir;
55
use rustc_hir::def::DefKind;
6-
use rustc_hir::intravisit::Visitor;
76
use rustc_hir::lang_items::LangItem;
87
use rustc_hir_analysis::check::check_function_signature;
98
use rustc_infer::infer::RegionVariableOrigin;
@@ -50,7 +49,9 @@ pub(super) fn check_fn<'a, 'tcx>(
5049

5150
let span = body.value.span;
5251

53-
GatherLocalsVisitor::new(fcx).visit_body(body);
52+
for param in body.params {
53+
GatherLocalsVisitor::gather_from_param(fcx, param);
54+
}
5455

5556
// C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
5657
// (as it's created inside the body itself, not passed in from outside).

compiler/rustc_hir_typeck/src/expr.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ use rustc_errors::{
1616
};
1717
use rustc_hir::def::{CtorKind, DefKind, Res};
1818
use rustc_hir::def_id::DefId;
19-
use rustc_hir::intravisit::Visitor;
2019
use rustc_hir::lang_items::LangItem;
2120
use rustc_hir::{ExprKind, HirId, QPath};
2221
use rustc_hir_analysis::NoVariantNamed;
@@ -50,8 +49,8 @@ use crate::errors::{
5049
YieldExprOutsideOfCoroutine,
5150
};
5251
use crate::{
53-
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, Needs, TupleArgumentsFlag, cast,
54-
fatally_break_rust, report_unexpected_variant_res, type_error_struct,
52+
BreakableCtxt, CoroutineTypes, Diverges, FnCtxt, GatherLocalsVisitor, Needs,
53+
TupleArgumentsFlag, cast, fatally_break_rust, report_unexpected_variant_res, type_error_struct,
5554
};
5655

5756
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1518,11 +1517,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15181517
let_expr: &'tcx hir::LetExpr<'tcx>,
15191518
hir_id: HirId,
15201519
) -> Ty<'tcx> {
1520+
GatherLocalsVisitor::gather_from_let_expr(self, let_expr, hir_id);
1521+
15211522
// for let statements, this is done in check_stmt
15221523
let init = let_expr.init;
15231524
self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression");
1525+
15241526
// otherwise check exactly as a let statement
15251527
self.check_decl((let_expr, hir_id).into());
1528+
15261529
// but return a bool, for this is a boolean expression
15271530
if let ast::Recovered::Yes(error_guaranteed) = let_expr.recovered {
15281531
self.set_tainted_by_errors(error_guaranteed);
@@ -1827,7 +1830,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
18271830
// Create a new function context.
18281831
let def_id = block.def_id;
18291832
let fcx = FnCtxt::new(self, self.param_env, def_id);
1830-
crate::GatherLocalsVisitor::new(&fcx).visit_body(body);
18311833

18321834
let ty = fcx.check_expr_with_expectation(body.value, expected);
18331835
fcx.require_type_is_sized(ty, body.value.span, ObligationCauseCode::SizedConstOrStatic);

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ use crate::method::probe::IsSuggestion;
3737
use crate::method::probe::Mode::MethodCall;
3838
use crate::method::probe::ProbeScope::TraitsInScope;
3939
use crate::{
40-
BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy, Needs, TupleArgumentsFlag, errors,
41-
struct_span_code_err,
40+
BreakableCtxt, Diverges, Expectation, FnCtxt, GatherLocalsVisitor, LoweredTy, Needs,
41+
TupleArgumentsFlag, errors, struct_span_code_err,
4242
};
4343

4444
rustc_index::newtype_index! {
@@ -1765,6 +1765,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17651765

17661766
/// Type check a `let` statement.
17671767
fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
1768+
GatherLocalsVisitor::gather_from_local(self, local);
1769+
17681770
let ty = self.check_decl(local.into());
17691771
self.write_ty(local.hir_id, ty);
17701772
if local.pat.is_never_pattern() {

compiler/rustc_hir_typeck/src/gather_locals.rs

+40-11
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@ impl<'a> From<(&'a hir::LetExpr<'a>, HirId)> for Declaration<'a> {
5555
}
5656
}
5757

58+
/// The `GatherLocalsVisitor` is responsible for initializing local variable types
59+
/// in the [`ty::TypeckResults`] for all subpatterns in statements and expressions
60+
/// like `let`, `match`, and params of function bodies. It also adds `Sized` bounds
61+
/// for these types (with exceptions for unzied feature gates like `unsized_fn_params`).
62+
///
63+
/// Failure to visit locals will cause an ICE in writeback when the local's type is
64+
/// resolved. Visiting locals twice will ICE in the `GatherLocalsVisitor`, since it
65+
/// will overwrite the type previously stored in the local.
5866
pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
5967
fcx: &'a FnCtxt<'a, 'tcx>,
6068
// parameters are special cases of patterns, but we want to handle them as
@@ -63,22 +71,50 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
6371
outermost_fn_param_pat: Option<(Span, HirId)>,
6472
}
6573

74+
// N.B. additional `gather_*` functions should be careful to only walk the pattern
75+
// for new expressions, since visiting sub-expressions or nested bodies may initialize
76+
// locals which are not conceptually owned by the gathered statement or expression.
6677
impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
67-
pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self {
68-
Self { fcx, outermost_fn_param_pat: None }
78+
pub(crate) fn gather_from_local(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::LetStmt<'tcx>) {
79+
let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
80+
visitor.declare(local.into());
81+
visitor.visit_pat(local.pat);
82+
}
83+
84+
pub(crate) fn gather_from_let_expr(
85+
fcx: &'a FnCtxt<'a, 'tcx>,
86+
let_expr: &'tcx hir::LetExpr<'tcx>,
87+
expr_hir_id: hir::HirId,
88+
) {
89+
let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
90+
visitor.declare((let_expr, expr_hir_id).into());
91+
visitor.visit_pat(let_expr.pat);
92+
}
93+
94+
pub(crate) fn gather_from_param(fcx: &'a FnCtxt<'a, 'tcx>, param: &'tcx hir::Param<'tcx>) {
95+
let mut visitor = GatherLocalsVisitor {
96+
fcx,
97+
outermost_fn_param_pat: Some((param.ty_span, param.hir_id)),
98+
};
99+
visitor.visit_pat(param.pat);
100+
}
101+
102+
pub(crate) fn gather_from_arm(fcx: &'a FnCtxt<'a, 'tcx>, local: &'tcx hir::Arm<'tcx>) {
103+
let mut visitor = GatherLocalsVisitor { fcx, outermost_fn_param_pat: None };
104+
visitor.visit_pat(local.pat);
69105
}
70106

71107
fn assign(&mut self, span: Span, nid: HirId, ty_opt: Option<Ty<'tcx>>) -> Ty<'tcx> {
72108
match ty_opt {
73109
None => {
74110
// Infer the variable's type.
75111
let var_ty = self.fcx.next_ty_var(span);
76-
self.fcx.locals.borrow_mut().insert(nid, var_ty);
112+
assert_eq!(self.fcx.locals.borrow_mut().insert(nid, var_ty), None);
77113
var_ty
78114
}
79115
Some(typ) => {
80116
// Take type that the user specified.
81-
self.fcx.locals.borrow_mut().insert(nid, typ);
117+
assert_eq!(self.fcx.locals.borrow_mut().insert(nid, typ), None);
82118
typ
83119
}
84120
}
@@ -133,13 +169,6 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
133169
intravisit::walk_expr(self, expr)
134170
}
135171

136-
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
137-
let old_outermost_fn_param_pat =
138-
self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id));
139-
intravisit::walk_param(self, param);
140-
self.outermost_fn_param_pat = old_outermost_fn_param_pat;
141-
}
142-
143172
// Add pattern bindings.
144173
fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) {
145174
if let PatKind::Binding(_, _, ident, _) = p.kind {

compiler/rustc_hir_typeck/src/lib.rs

-4
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ use rustc_errors::codes::*;
4646
use rustc_errors::{Applicability, ErrorGuaranteed, pluralize, struct_span_code_err};
4747
use rustc_hir as hir;
4848
use rustc_hir::def::{DefKind, Res};
49-
use rustc_hir::intravisit::Visitor;
5049
use rustc_hir::{HirId, HirIdMap, Node};
5150
use rustc_hir_analysis::check::check_abi;
5251
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
@@ -191,9 +190,6 @@ fn typeck_with_inspect<'tcx>(
191190
let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id)));
192191
fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code);
193192

194-
// Gather locals in statics (because of block expressions).
195-
GatherLocalsVisitor::new(&fcx).visit_body(body);
196-
197193
fcx.check_expr_coercible_to_type(body.value, expected_type, None);
198194

199195
fcx.write_ty(id, expected_type);

tests/ui/async-await/async-closures/def-path.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ LL | let x = async || {};
55
| -- the expected `async` closure body
66
LL |
77
LL | let () = x();
8-
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
8+
| ^^ --- this expression has type `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}`
99
| |
1010
| expected `async` closure body, found `()`
1111
|
12-
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?6t}`
12+
= note: expected `async` closure body `{static main::{closure#0}::{closure#0}<?17t> upvar_tys=?16t resume_ty=ResumeTy yield_ty=() return_ty=() witness=?5t}`
1313
found unit type `()`
1414

1515
error: aborting due to 1 previous error

tests/ui/closures/print/closure-print-generic-trim-off-verbose-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | let c1 : () = c;
99
| expected due to this
1010
|
1111
= note: expected unit type `()`
12-
found closure `{mod1::f<T>::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}`
12+
found closure `{mod1::f<T>::{closure#0} closure_kind_ty=?7t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?6t}`
1313
help: use parentheses to call this closure
1414
|
1515
LL | let c1 : () = c();

tests/ui/closures/print/closure-print-generic-verbose-2.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ LL | let c1 : () = c;
99
| expected due to this
1010
|
1111
= note: expected unit type `()`
12-
found closure `{f<T>::{closure#0} closure_kind_ty=?8t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?7t}`
12+
found closure `{f<T>::{closure#0} closure_kind_ty=?7t closure_sig_as_fn_ptr_ty=extern "rust-call" fn(()) upvar_tys=?6t}`
1313
help: use parentheses to call this closure
1414
|
1515
LL | let c1 : () = c();

tests/ui/const-generics/const-arg-in-const-arg.min.stderr

+35-35
Original file line numberDiff line numberDiff line change
@@ -240,41 +240,6 @@ note: the late bound lifetime parameter is introduced here
240240
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
241241
| ^^
242242

243-
error[E0747]: unresolved item provided when a constant was expected
244-
--> $DIR/const-arg-in-const-arg.rs:36:24
245-
|
246-
LL | let _: Foo<{ bar::<N>() }>;
247-
| ^
248-
|
249-
help: if this generic argument was intended as a const parameter, surround it with braces
250-
|
251-
LL | let _: Foo<{ bar::<{ N }>() }>;
252-
| + +
253-
254-
error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
255-
--> $DIR/const-arg-in-const-arg.rs:38:24
256-
|
257-
LL | let _: Foo<{ faz::<'a>(&()) }>;
258-
| ^^
259-
|
260-
note: the late bound lifetime parameter is introduced here
261-
--> $DIR/const-arg-in-const-arg.rs:10:14
262-
|
263-
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
264-
| ^^
265-
266-
error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
267-
--> $DIR/const-arg-in-const-arg.rs:41:24
268-
|
269-
LL | let _: Foo<{ faz::<'b>(&()) }>;
270-
| ^^
271-
|
272-
note: the late bound lifetime parameter is introduced here
273-
--> $DIR/const-arg-in-const-arg.rs:10:14
274-
|
275-
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
276-
| ^^
277-
278243
error: constant expression depends on a generic parameter
279244
--> $DIR/const-arg-in-const-arg.rs:25:17
280245
|
@@ -326,6 +291,41 @@ note: the late bound lifetime parameter is introduced here
326291
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
327292
| ^^
328293

294+
error[E0747]: unresolved item provided when a constant was expected
295+
--> $DIR/const-arg-in-const-arg.rs:36:24
296+
|
297+
LL | let _: Foo<{ bar::<N>() }>;
298+
| ^
299+
|
300+
help: if this generic argument was intended as a const parameter, surround it with braces
301+
|
302+
LL | let _: Foo<{ bar::<{ N }>() }>;
303+
| + +
304+
305+
error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
306+
--> $DIR/const-arg-in-const-arg.rs:38:24
307+
|
308+
LL | let _: Foo<{ faz::<'a>(&()) }>;
309+
| ^^
310+
|
311+
note: the late bound lifetime parameter is introduced here
312+
--> $DIR/const-arg-in-const-arg.rs:10:14
313+
|
314+
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
315+
| ^^
316+
317+
error[E0794]: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
318+
--> $DIR/const-arg-in-const-arg.rs:41:24
319+
|
320+
LL | let _: Foo<{ faz::<'b>(&()) }>;
321+
| ^^
322+
|
323+
note: the late bound lifetime parameter is introduced here
324+
--> $DIR/const-arg-in-const-arg.rs:10:14
325+
|
326+
LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
327+
| ^^
328+
329329
error[E0747]: unresolved item provided when a constant was expected
330330
--> $DIR/const-arg-in-const-arg.rs:45:27
331331
|

tests/ui/const-generics/generic_const_exprs/dependence_lint.gce.stderr

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,38 @@
1-
error: overly complex generic constant
2-
--> $DIR/dependence_lint.rs:21:17
3-
|
4-
LL | let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
6-
|
7-
= help: consider moving this anonymous constant into a `const` function
8-
91
error: unconstrained generic constant
10-
--> $DIR/dependence_lint.rs:14:12
2+
--> $DIR/dependence_lint.rs:10:9
113
|
12-
LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
13-
| ^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
5+
| ^^^^^^^^^^^^^^^^^^^
146
|
157
help: try adding a `where` bound
168
|
179
LL | fn foo<T>() where [(); size_of::<*mut T>()]: {
1810
| ++++++++++++++++++++++++++++++++
1911

12+
error: overly complex generic constant
13+
--> $DIR/dependence_lint.rs:17:9
14+
|
15+
LL | [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
16+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
17+
|
18+
= help: consider moving this anonymous constant into a `const` function
19+
2020
error: unconstrained generic constant
21-
--> $DIR/dependence_lint.rs:10:9
21+
--> $DIR/dependence_lint.rs:14:12
2222
|
23-
LL | [0; size_of::<*mut T>()]; // lint on stable, error with `generic_const_exprs`
24-
| ^^^^^^^^^^^^^^^^^^^
23+
LL | let _: [u8; size_of::<*mut T>()]; // error on stable, error with gce
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
2525
|
2626
help: try adding a `where` bound
2727
|
2828
LL | fn foo<T>() where [(); size_of::<*mut T>()]: {
2929
| ++++++++++++++++++++++++++++++++
3030

3131
error: overly complex generic constant
32-
--> $DIR/dependence_lint.rs:17:9
32+
--> $DIR/dependence_lint.rs:21:17
3333
|
34-
LL | [0; if false { size_of::<T>() } else { 3 }]; // lint on stable, error with gce
35-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
34+
LL | let _: [u8; if true { size_of::<T>() } else { 3 }]; // error on stable, error with gce
35+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ control flow is not supported in generic constants
3636
|
3737
= help: consider moving this anonymous constant into a `const` function
3838

0 commit comments

Comments
 (0)