Skip to content

Commit c3df176

Browse files
committed
Replace confusing "return statement outside of function body" message and emit new error code
1 parent 64a965e commit c3df176

6 files changed

Lines changed: 113 additions & 7 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
The `?` operator was used to return from a `const` or `static` initializer.
2+
3+
Erroneous code example:
4+
5+
```compile_fail,E0807
6+
const fn foo() -> Result<(), ()> { Ok(()) }
7+
8+
fn main() -> Result<(), ()> {
9+
// error: the `?` operator cannot be used to return from a `const` initializer
10+
const A: () = foo()?;
11+
Ok(A)
12+
}
13+
```
14+
15+
To fix this error, either use the `?` operator outside of
16+
the `const` initializer:
17+
18+
```
19+
const fn foo() -> Result<(), ()> { Ok(()) }
20+
21+
fn main() -> Result<(), ()> {
22+
// store the `Result` in the const and apply `?` later
23+
const A: Result<(), ()> = foo();
24+
Ok(A?)
25+
}
26+
```
27+
28+
or handle each arm explicitly without returning:
29+
30+
```
31+
const fn foo() -> Result<(), ()> { Ok(()) }
32+
33+
fn main() -> Result<(), ()> {
34+
const A: () = match foo() {
35+
Ok(x) => x,
36+
Err(_) => panic!()
37+
};
38+
Ok(A)
39+
}
40+
```
41+
42+
or use `let` instead of `const`:
43+
44+
```
45+
const fn foo() -> Result<(), ()> { Ok(()) }
46+
47+
fn main() -> Result<(), ()> {
48+
let a = foo()?;
49+
Ok(a)
50+
}
51+
```

compiler/rustc_error_codes/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,7 @@ macro_rules! error_codes {
545545
0804,
546546
0805,
547547
0806,
548+
0807,
548549
);
549550
)
550551
}

compiler/rustc_hir_typeck/src/errors.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ impl IntoDiagArg for ReturnLikeStatementKind {
100100
}
101101
}
102102

103+
#[derive(Diagnostic)]
104+
#[diag("the `?` operator cannot be used to return from a `{$keyword}` initializer", code = E0807)]
105+
pub(crate) struct QuestionMarkInConst {
106+
#[primary_span]
107+
pub span: Span,
108+
pub keyword: &'static str,
109+
}
110+
103111
#[derive(Diagnostic)]
104112
#[diag("functions with the \"rust-call\" ABI must take a single non-self tuple argument")]
105113
pub(crate) struct RustCallIncorrectArgs {

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_hir as hir;
2020
use rustc_hir::def::{CtorKind, DefKind, Res};
2121
use rustc_hir::def_id::DefId;
2222
use rustc_hir::lang_items::LangItem;
23-
use rustc_hir::{ExprKind, HirId, QPath, find_attr, is_range_literal};
23+
use rustc_hir::{ConstContext, ExprKind, HirId, QPath, find_attr, is_range_literal};
2424
use rustc_hir_analysis::NoVariantNamed;
2525
use rustc_hir_analysis::errors::NoFieldOnType;
2626
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer as _;
@@ -44,8 +44,8 @@ use crate::errors::{
4444
AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr,
4545
BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer,
4646
FunctionalRecordUpdateOnNonStruct, HelpUseLatestEdition, NakedAsmOutsideNakedFn,
47-
NoFieldOnVariant, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive,
48-
TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
47+
NoFieldOnVariant, QuestionMarkInConst, ReturnLikeStatementKind, ReturnStmtOutsideOfFnBody,
48+
StructExprNonExhaustive, TypeMismatchFruTypo, YieldExprOutsideOfCoroutine,
4949
};
5050
use crate::op::contains_let_in_chain;
5151
use crate::{
@@ -862,12 +862,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
862862
expr: &'tcx hir::Expr<'tcx>,
863863
) -> Ty<'tcx> {
864864
if self.ret_coercion.is_none() {
865-
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
865+
let expectation = if let Some(desugar_kind) = expr.span.desugaring_kind()
866+
&& desugar_kind == DesugaringKind::QuestionMark
867+
&& let Some(ccx) = self.tcx.hir_body_const_context(self.body_id)
868+
&& matches!(ccx, ConstContext::Const { .. } | ConstContext::Static(_))
869+
{
870+
let guaranteed = self
871+
.tcx
872+
.dcx()
873+
.emit_err(QuestionMarkInConst { span: expr.span, keyword: ccx.keyword_name() });
874+
// Suppresses incorrect and unnecessary "E0283: type annotations needed"
875+
ExpectHasType(Ty::new_error(self.tcx, guaranteed))
876+
} else {
877+
self.emit_return_outside_of_fn_body(expr, ReturnLikeStatementKind::Return);
878+
NoExpectation
879+
};
866880

867881
if let Some(e) = expr_opt {
868882
// We still have to type-check `e` (issue #86188), but calling
869883
// `check_return_expr` only works inside fn bodies.
870-
self.check_expr(e);
884+
self.check_expr_with_expectation(e, expectation);
871885
}
872886
} else if let Some(e) = expr_opt {
873887
if self.ret_coercion_span.get().is_none() {
@@ -986,7 +1000,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9861000
///
9871001
/// `expr` is the `return` (`become`) "statement", `kind` is the kind of the statement
9881002
/// either `Return` or `Become`.
989-
fn emit_return_outside_of_fn_body(&self, expr: &hir::Expr<'_>, kind: ReturnLikeStatementKind) {
1003+
fn emit_return_outside_of_fn_body(
1004+
&self,
1005+
expr: &hir::Expr<'_>,
1006+
kind: ReturnLikeStatementKind,
1007+
) -> ErrorGuaranteed {
9901008
let mut err = ReturnStmtOutsideOfFnBody {
9911009
span: expr.span,
9921010
encl_body_span: None,
@@ -1027,7 +1045,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10271045
err.encl_fn_span = Some(*encl_fn_span);
10281046
}
10291047

1030-
self.dcx().emit_err(err);
1048+
self.dcx().emit_err(err)
10311049
}
10321050

10331051
fn point_at_return_for_opaque_ty_error(
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//! Regression test for <https://github.com/rust-lang/rust/issues/156200>
2+
3+
//! Fixes misleading "return statement outside of function body" message emitted
4+
//! when `?` was used in a `const`/`static` initializer, even if that initializer
5+
//! was within a function body
6+
7+
const fn foo() -> Result<(), ()> { Ok(()) }
8+
9+
fn main() -> Result<(), ()> {
10+
const A: () = foo()?; //~ ERROR the `?` operator cannot be used to return from a `const` initializer
11+
static B: () = foo()?; //~ ERROR the `?` operator cannot be used to return from a `static` initializer
12+
Ok(())
13+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0807]: the `?` operator cannot be used to return from a `const` initializer
2+
--> $DIR/question-mark-returning-from-initializer.rs:4:24
3+
|
4+
LL | const A: () = foo()?;
5+
| ^
6+
7+
error[E0807]: the `?` operator cannot be used to return from a `static` initializer
8+
--> $DIR/question-mark-returning-from-initializer.rs:5:25
9+
|
10+
LL | static B: () = foo()?;
11+
| ^
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0807`.

0 commit comments

Comments
 (0)