From 9f04fc67725a964d6bf6f7650608a62fe2b2f38d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 31 May 2024 07:42:37 +0000 Subject: [PATCH 1/2] Create const blocks on the fly during typeck instead of waiting until writeback. This allows us to spawn a nested FnCtxt, creating a barrier that control flow statements cannot cross. --- compiler/rustc_ast_lowering/src/expr.rs | 4 +- compiler/rustc_hir/src/hir.rs | 1 + compiler/rustc_hir_typeck/src/expr.rs | 33 ++++++-- compiler/rustc_hir_typeck/src/writeback.rs | 12 +-- .../inline-const/cross_const_control_flow.rs | 40 ++++++++++ .../cross_const_control_flow.stderr | 76 +++++++++++++++++++ 6 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 tests/ui/inline-const/cross_const_control_flow.rs create mode 100644 tests/ui/inline-const/cross_const_control_flow.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb206a09be313..ce392fc9cdaf6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -76,7 +76,9 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Array(exprs) => hir::ExprKind::Array(self.lower_exprs(exprs)), ExprKind::ConstBlock(c) => { self.has_inline_consts = true; - hir::ExprKind::ConstBlock(self.lower_expr(c)) + self.with_new_scopes(e.span, |this| { + hir::ExprKind::ConstBlock(this.lower_expr(c)) + }) } ExprKind::Repeat(expr, count) => { let expr = self.lower_expr(expr); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e64f7aeb11b3e..8f275faeda7fa 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2131,6 +2131,7 @@ impl LoopSource { } #[derive(Copy, Clone, Debug, PartialEq, HashStable_Generic)] +// FIXME: eagerly report these errors and remove this enum and the wfcheck. pub enum LoopIdError { OutsideLoopScope, UnlabeledCfInWhileCondition, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 061d9507a35c7..7ce765f624886 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -335,7 +335,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref block) => self.check_expr_with_expectation(block, expected), + ExprKind::ConstBlock(ref block) => { + self.check_expr_const_block(block, expr.hir_id, expr.span, expected) + } ExprKind::Repeat(element, ref count) => { self.check_expr_repeat(element, count, expected, expr) } @@ -903,15 +905,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We are inside a function body, so reporting "return statement // outside of function body" needs an explanation. - let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id); - // If this didn't hold, we would not have to report an error in // the first place. - assert_ne!(encl_item_id.def_id, encl_body_owner_id); + assert_ne!(encl_item_id.def_id, self.body_id); - let encl_body = self.tcx.hir().body_owned_by(encl_body_owner_id); - - err.encl_body_span = Some(encl_body.value.span); + err.encl_body_span = Some(self.tcx.def_span(self.body_id)); err.encl_fn_span = Some(*encl_fn_span); } @@ -1458,6 +1456,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + fn check_expr_const_block( + &self, + block: &'tcx hir::Expr<'tcx>, + hir_id: HirId, + span: Span, + expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let feed = self.tcx().create_def(self.body_id, kw::Empty, DefKind::InlineConst); + feed.def_span(span); + feed.local_def_id_to_hir_id(hir_id); + self.typeck_results.borrow_mut().inline_consts.insert(hir_id.local_id, feed.def_id()); + + // Create a new function context. + let fcx = FnCtxt::new(self, self.param_env, feed.key()); + + let ty = fcx.check_expr_with_expectation(block, expected); + fcx.require_type_is_sized(ty, block.span, ObligationCauseCode::ConstSized); + fcx.write_ty(block.hir_id, ty); + ty + } + fn check_expr_repeat( &self, element: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 31caa52d26710..727de5934b7d7 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -3,7 +3,6 @@ // generic parameters. use crate::FnCtxt; -use hir::def::DefKind; use rustc_data_structures::unord::ExtendUnord; use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; @@ -17,7 +16,7 @@ use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::visit::TypeVisitableExt; use rustc_middle::ty::TypeSuperFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::solve; use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; @@ -84,6 +83,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { wbcx.typeck_results.treat_byte_string_as_slice = mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); + wbcx.typeck_results.inline_consts = + mem::take(&mut self.typeck_results.borrow_mut().inline_consts); + debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); self.tcx.arena.alloc(wbcx.typeck_results) @@ -296,12 +298,6 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { hir::ExprKind::Field(..) | hir::ExprKind::OffsetOf(..) => { self.visit_field_id(e.hir_id); } - hir::ExprKind::ConstBlock(_) => { - let feed = self.tcx().create_def(self.fcx.body_id, kw::Empty, DefKind::InlineConst); - feed.def_span(e.span); - feed.local_def_id_to_hir_id(e.hir_id); - self.typeck_results.inline_consts.insert(e.hir_id.local_id, feed.def_id()); - } _ => {} } diff --git a/tests/ui/inline-const/cross_const_control_flow.rs b/tests/ui/inline-const/cross_const_control_flow.rs new file mode 100644 index 0000000000000..8d6c5b99acf38 --- /dev/null +++ b/tests/ui/inline-const/cross_const_control_flow.rs @@ -0,0 +1,40 @@ +//@edition:2021 + +fn foo() { + const { return } + //~^ ERROR: return statement outside of function body +} + +fn labelled_block_break() { + 'a: { const { break 'a } } + //~^ ERROR: `break` outside of a loop or labeled block + //~| ERROR: use of unreachable label +} + +fn loop_break() { + loop { + const { break } + //~^ ERROR: `break` outside of a loop or labeled block + } +} + +fn continue_to_labelled_block() { + 'a: { const { continue 'a } } + //~^ ERROR: `continue` outside of a loop + //~| ERROR: use of unreachable label +} + +fn loop_continue() { + loop { + const { continue } + //~^ ERROR: `continue` outside of a loop + } +} + +async fn await_across_const_block() { + const { async {}.await } + //~^ ERROR: mismatched types + //~| ERROR: yield expression outside of coroutine literal +} + +fn main() {} diff --git a/tests/ui/inline-const/cross_const_control_flow.stderr b/tests/ui/inline-const/cross_const_control_flow.stderr new file mode 100644 index 0000000000000..f5de20c2ff150 --- /dev/null +++ b/tests/ui/inline-const/cross_const_control_flow.stderr @@ -0,0 +1,76 @@ +error[E0767]: use of unreachable label `'a` + --> $DIR/cross_const_control_flow.rs:9:25 + | +LL | 'a: { const { break 'a } } + | -- ^^ unreachable label `'a` + | | + | unreachable label defined here + | + = note: labels are unreachable through functions, closures, async blocks and modules + +error[E0767]: use of unreachable label `'a` + --> $DIR/cross_const_control_flow.rs:22:28 + | +LL | 'a: { const { continue 'a } } + | -- ^^ unreachable label `'a` + | | + | unreachable label defined here + | + = note: labels are unreachable through functions, closures, async blocks and modules + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/cross_const_control_flow.rs:9:19 + | +LL | 'a: { const { break 'a } } + | ^^^^^^^^ cannot `break` outside of a loop or labeled block + +error[E0268]: `break` outside of a loop or labeled block + --> $DIR/cross_const_control_flow.rs:16:17 + | +LL | const { break } + | ^^^^^ cannot `break` outside of a loop or labeled block + +error[E0268]: `continue` outside of a loop + --> $DIR/cross_const_control_flow.rs:22:19 + | +LL | 'a: { const { continue 'a } } + | ^^^^^^^^^^^ cannot `continue` outside of a loop + +error[E0268]: `continue` outside of a loop + --> $DIR/cross_const_control_flow.rs:29:17 + | +LL | const { continue } + | ^^^^^^^^ cannot `continue` outside of a loop + +error[E0627]: yield expression outside of coroutine literal + --> $DIR/cross_const_control_flow.rs:35:22 + | +LL | const { async {}.await } + | ^^^^^ + +error[E0308]: mismatched types + --> $DIR/cross_const_control_flow.rs:35:22 + | +LL | async fn await_across_const_block() { + | _____________________________________- +LL | | const { async {}.await } + | | ^^^^^ expected `ResumeTy`, found `()` +LL | | +LL | | +LL | | } + | |_- expected due to this parameter type + +error[E0572]: return statement outside of function body + --> $DIR/cross_const_control_flow.rs:4:13 + | +LL | / fn foo() { +LL | | const { return } + | | --------^^^^^^-- the return is part of this body... +LL | | +LL | | } + | |_- ...not the enclosing function body + +error: aborting due to 9 previous errors + +Some errors have detailed explanations: E0268, E0308, E0572, E0627, E0767. +For more information about an error, try `rustc --explain E0268`. From 5e3f5787b6fa0eefc97282e11bf64319766239b0 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 31 May 2024 07:51:06 +0000 Subject: [PATCH 2/2] Properly report await errors --- compiler/rustc_ast_lowering/src/expr.rs | 5 ++- .../inline-const/cross_const_control_flow.rs | 3 +- .../cross_const_control_flow.stderr | 31 +++++++------------ 3 files changed, 16 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ce392fc9cdaf6..1d7ff80312193 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -77,7 +77,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::ConstBlock(c) => { self.has_inline_consts = true; self.with_new_scopes(e.span, |this| { - hir::ExprKind::ConstBlock(this.lower_expr(c)) + let coroutine_kind = this.coroutine_kind.take(); + let e = hir::ExprKind::ConstBlock(this.lower_expr(c)); + this.coroutine_kind = coroutine_kind; + e }) } ExprKind::Repeat(expr, count) => { diff --git a/tests/ui/inline-const/cross_const_control_flow.rs b/tests/ui/inline-const/cross_const_control_flow.rs index 8d6c5b99acf38..4c5c943d56b34 100644 --- a/tests/ui/inline-const/cross_const_control_flow.rs +++ b/tests/ui/inline-const/cross_const_control_flow.rs @@ -33,8 +33,7 @@ fn loop_continue() { async fn await_across_const_block() { const { async {}.await } - //~^ ERROR: mismatched types - //~| ERROR: yield expression outside of coroutine literal + //~^ ERROR: `await` is only allowed inside `async` functions and blocks } fn main() {} diff --git a/tests/ui/inline-const/cross_const_control_flow.stderr b/tests/ui/inline-const/cross_const_control_flow.stderr index f5de20c2ff150..9e405178ded8c 100644 --- a/tests/ui/inline-const/cross_const_control_flow.stderr +++ b/tests/ui/inline-const/cross_const_control_flow.stderr @@ -18,6 +18,15 @@ LL | 'a: { const { continue 'a } } | = note: labels are unreachable through functions, closures, async blocks and modules +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/cross_const_control_flow.rs:35:22 + | +LL | const { async {}.await } + | -----------------^^^^^-- + | | | + | | only allowed inside `async` functions and blocks + | this is not `async` + error[E0268]: `break` outside of a loop or labeled block --> $DIR/cross_const_control_flow.rs:9:19 | @@ -42,24 +51,6 @@ error[E0268]: `continue` outside of a loop LL | const { continue } | ^^^^^^^^ cannot `continue` outside of a loop -error[E0627]: yield expression outside of coroutine literal - --> $DIR/cross_const_control_flow.rs:35:22 - | -LL | const { async {}.await } - | ^^^^^ - -error[E0308]: mismatched types - --> $DIR/cross_const_control_flow.rs:35:22 - | -LL | async fn await_across_const_block() { - | _____________________________________- -LL | | const { async {}.await } - | | ^^^^^ expected `ResumeTy`, found `()` -LL | | -LL | | -LL | | } - | |_- expected due to this parameter type - error[E0572]: return statement outside of function body --> $DIR/cross_const_control_flow.rs:4:13 | @@ -70,7 +61,7 @@ LL | | LL | | } | |_- ...not the enclosing function body -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors -Some errors have detailed explanations: E0268, E0308, E0572, E0627, E0767. +Some errors have detailed explanations: E0268, E0572, E0728, E0767. For more information about an error, try `rustc --explain E0268`.