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
51 changes: 51 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,51 @@
The `?` operator was used to return from a `const` or `static` initializer.

Erroneous code example:

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

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

To fix this error, either use the `?` operator outside of
the `const` 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 handle each arm explicitly without returning:

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

fn main() -> Result<(), ()> {
const A: () = match foo() {
Ok(x) => x,
Err(_) => panic!()
};
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 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 a `const` initializer
static B: () = foo()?; //~ ERROR the `?` operator cannot be used to return from 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 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 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