Skip to content

Commit

Permalink
Check that closure's by-value captures are sized
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Sep 23, 2023
1 parent 2d08657 commit c21867f
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 0 deletions.
15 changes: 15 additions & 0 deletions compiler/rustc_hir_typeck/src/upvar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_infer::infer::UpvarRegion;
use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind};
use rustc_middle::mir::FakeReadCause;
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::{
self, ClosureSizeProfileData, Ty, TyCtxt, TypeckResults, UpvarArgs, UpvarCapture,
};
Expand Down Expand Up @@ -295,6 +296,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let final_upvar_tys = self.final_upvar_tys(closure_def_id);
debug!(?closure_hir_id, ?args, ?final_upvar_tys);

if self.tcx.features().unsized_locals || self.tcx.features().unsized_fn_params {
for capture in
self.typeck_results.borrow().closure_min_captures_flattened(closure_def_id)
{
if let UpvarCapture::ByValue = capture.info.capture_kind {
self.require_type_is_sized(
capture.place.ty(),
capture.get_path_span(self.tcx),
ObligationCauseCode::SizedClosureCapture(closure_def_id),
);
}
}
}

// Build a tuple (U0..Un) of the final upvar types U0..Un
// and unify the upvar tuple type in the closure with it:
let final_tupled_upvars_type = Ty::new_tup(self.tcx, &final_upvar_tys);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ pub enum ObligationCauseCode<'tcx> {
SizedYieldType,
/// Inline asm operand type must be `Sized`.
InlineAsmSized,
/// Captured closure type type must be `Sized`.
SizedClosureCapture(LocalDefId),
/// `[expr; N]` requires `type_of(expr): Copy`.
RepeatElementCopy {
/// If element is a `const fn` we display a help message suggesting to move the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3007,6 +3007,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ObligationCauseCode::InlineAsmSized => {
err.note("all inline asm arguments must have a statically known size");
}
ObligationCauseCode::SizedClosureCapture(closure_def_id) => {
err.note("all values captured by value by a closure must have a statically known size");
let hir::ExprKind::Closure(closure) = self.tcx.hir().get_by_def_id(closure_def_id).expect_expr().kind else {
bug!("expected closure in SizedClosureCapture obligation");
};
if let hir::CaptureBy::Value = closure.capture_clause
&& let Some(span) = closure.fn_arg_span
{
err.span_label(span, "this closure captures all values by move");
}
}
ObligationCauseCode::ConstPatternStructural => {
err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`");
}
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/closures/capture-unsized-by-move.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// compile-flags: --crate-type=lib

#![feature(unsized_fn_params)]

pub fn f(k: dyn std::fmt::Display) {
let k2 = move || {
k.to_string();
//~^ ERROR the size for values of type `(dyn std::fmt::Display + 'static)` cannot be known at compilation time
};
}
14 changes: 14 additions & 0 deletions tests/ui/closures/capture-unsized-by-move.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0277]: the size for values of type `(dyn std::fmt::Display + 'static)` cannot be known at compilation time
--> $DIR/capture-unsized-by-move.rs:7:9
|
LL | let k2 = move || {
| -- this closure captures all values by move
LL | k.to_string();
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::fmt::Display + 'static)`
= note: all values captured by value by a closure must have a statically known size

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
10 changes: 10 additions & 0 deletions tests/ui/closures/capture-unsized-by-ref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// build-pass
// compile-flags: --crate-type=lib

#![feature(unsized_fn_params)]

pub fn f(k: dyn std::fmt::Display) {
let k2 = || {
k.to_string();
};
}

0 comments on commit c21867f

Please sign in to comment.