Skip to content
Draft
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
39 changes: 39 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0807.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
A keyword was used to move control flow out of a `const`
or `static` initializer.

This can occur in the following scenario.

A `?` was used to return from inside a const initializer:
```compile_fail,E0807
const fn foo() -> Result<(), ()> { Ok(()) }

fn main() -> Result<(), ()> {
// error: the `?` operator cannot be used to return from inside a `const` initializer
const A: () = foo()?;
Ok(A)
}
```

To fix this error, either use the keyword or operator outside
the `const` or `static` initializer:

```
const fn foo() -> Result<(), ()> { Ok(()) }

fn main() -> Result<(), ()> {
// store the `Result` in the const and apply `?` later
const A: Result<(), ()> = foo();
Ok(A?)
}
```

or use `let` instead of `const`:

```
const fn foo() -> Result<(), ()> { Ok(()) }

fn main() -> Result<(), ()> {
let a = foo()?;
Ok(a)
}
```
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ macro_rules! error_codes {
0804,
0805,
0806,
0807,
);
)
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ impl IntoDiagArg for ReturnLikeStatementKind {
}
}

#[derive(Diagnostic)]
#[diag("the `?` operator cannot be used to return from inside a `{$keyword}` initializer", code = E0807)]
pub(crate) struct QuestionMarkInConst {
#[primary_span]
pub span: Span,
pub keyword: &'static str,
}

#[derive(Diagnostic)]
#[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")]
pub(crate) struct RustCallIncorrectArgs {
Expand Down
32 changes: 25 additions & 7 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
use rustc_hir::{ConstContext, ExprKind, HirId, QPath, find_attr, is_range_literal};
use rustc_hir_analysis::NoVariantNamed;
use rustc_hir_analysis::errors::NoFieldOnType;
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
Expand All @@ -44,8 +44,8 @@ use crate::errors::{
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn,
NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
NoFieldOnVariant, QuestionMarkInConst, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody,
StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
};
use crate::op::contains_let_in_chain;
use crate::{
Expand Down Expand Up @@ -862,12 +862,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
expr: &'tcx hir::Expr<'tcx>,
) -> Ty<'tcx> {
if self.ret_coercion.is_none() {
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
let expectation = if let Some(desugar_kind) = expr.span.desugaring_kind()
&& desugar_kind == DesugaringKind::QuestionMark
&& let Some(ccx) = self.tcx.hir_body_const_context(self.body_id)
&& matches!(ccx, ConstContext::Const { .. } | ConstContext::Static(_))
{
let guaranteed = self
.tcx
.dcx()
.emit_err(QuestionMarkInConst { span: expr.span, keyword: ccx.keyword_name() });
// Suppresses incorrect and unnecessary "E0283: type annotations needed"
ExpectHasType(Ty::new_error(self.tcx, guaranteed))
} else {
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
NoExpectation
};

if let Some(e) = expr_opt {
// We still have to type-check `e` (issue #86188), but calling
// `check_return_expr` only works inside fn bodies.
self.check_expr(e);
self.check_expr_with_expectation(e, expectation);
}
} else if let Some(e) = expr_opt {
if self.ret_coercion_span.get().is_none() {
Expand Down Expand Up @@ -986,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
///
/// `expr` is the `return` (`become`) "statement", `kind` is the kind of the statement
/// either `Return` or `Become`.
fn emit_return_outside_of_fn_body(&self, expr: &hir::Expr<'_>, kind: ReturnLikeStatementKind) {
fn emit_return_outside_of_fn_body(
&self,
expr: &hir::Expr<'_>,
kind: ReturnLikeStatementKind,
) -> ErrorGuaranteed {
let mut err = ReturnStmtOutsideOfFnBody {
span: expr.span,
encl_body_span: None,
Expand Down Expand Up @@ -1027,7 +1045,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err.encl_fn_span = Some(*encl_fn_span);
}

self.dcx().emit_err(err);
self.dcx().emit_err(err)
}

fn point_at_return_for_opaque_ty_error(
Expand Down
13 changes: 13 additions & 0 deletions tests/ui/consts/question-mark-returning-from-initializer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Regression test for <https://github.com/rust-lang/rust/issues/156200>

//! Fixes misleading "return statement outside of function body" message emitted
//! when `?` was used in a `const`/`static` initializer, even if that initializer
//! was within a function body

const fn foo() -> Result<(), ()> { Ok(()) }

fn main() -> Result<(), ()> {
const A: () = foo()?; //~ ERROR the `?` operator cannot be used to return from inside a `const` initializer
static B: () = foo()?; //~ ERROR the `?` operator cannot be used to return from inside a `static` initializer
Ok(())
}
15 changes: 15 additions & 0 deletions tests/ui/consts/question-mark-returning-from-initializer.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error[E0807]: the `?` operator cannot be used to return from inside a `const` initializer
--> $DIR/question-mark-returning-from-initializer.rs:10:24
|
LL | const A: () = foo()?;
| ^

error[E0807]: the `?` operator cannot be used to return from inside a `static` initializer
--> $DIR/question-mark-returning-from-initializer.rs:11:25
|
LL | static B: () = foo()?;
| ^

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0807`.
Loading