Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check that closure/generator's interior/capture types are sized #116081

Merged
merged 2 commits into from
Sep 23, 2023
Merged
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
21 changes: 20 additions & 1 deletion compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_infer::traits::{Obligation, TraitEngineExt as _};
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::traits::DefiningAnchor;
use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode};
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
use rustc_middle::ty::util::{Discr, IntTypeExt};
Expand Down Expand Up @@ -1626,6 +1626,25 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
fulfillment_cx.register_predicate_obligation(&infcx, obligation);
}

if (tcx.features().unsized_locals || tcx.features().unsized_fn_params)
&& let Some(generator) = tcx.mir_generator_witnesses(def_id)
{
for field_ty in generator.field_tys.iter() {
fulfillment_cx.register_bound(
&infcx,
param_env,
field_ty.ty,
tcx.require_lang_item(hir::LangItem::Sized, Some(field_ty.source_info.span)),
ObligationCause::new(
field_ty.source_info.span,
def_id,
ObligationCauseCode::SizedGeneratorInterior(def_id),
),
);
}
}

let errors = fulfillment_cx.select_all_or_error(&infcx);
debug!(?errors);
if !errors.is_empty() {
Expand Down
11 changes: 9 additions & 2 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.select_obligations_where_possible(|_| {});

let mut generators = self.deferred_generator_interiors.borrow_mut();
for (_, body_id, interior, kind) in generators.drain(..) {
crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
for (generator_def_id, body_id, interior, kind) in generators.drain(..) {
crate::generator_interior::resolve_interior(
self,
def_id,
generator_def_id,
body_id,
interior,
kind,
);
self.select_obligations_where_possible(|_| {});
}
}
Expand Down
14 changes: 13 additions & 1 deletion compiler/rustc_hir_typeck/src/generator_interior/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
use rustc_errors::{pluralize, DelayDm};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::hir_id::HirIdSet;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind};
use rustc_infer::infer::{DefineOpaqueTypes, RegionVariableOrigin};
use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData};
use rustc_middle::traits::ObligationCauseCode;
use rustc_middle::ty::fold::FnMutDelegate;
use rustc_middle::ty::{self, BoundVariableKind, RvalueScopes, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::symbol::sym;
Expand Down Expand Up @@ -188,6 +189,7 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
pub fn resolve_interior<'a, 'tcx>(
fcx: &'a FnCtxt<'a, 'tcx>,
def_id: DefId,
generator_def_id: LocalDefId,
body_id: hir::BodyId,
interior: Ty<'tcx>,
kind: hir::GeneratorKind,
Expand All @@ -214,6 +216,16 @@ pub fn resolve_interior<'a, 'tcx>(
// The types are already kept in insertion order.
let types = visitor.types;

if fcx.tcx.features().unsized_locals || fcx.tcx.features().unsized_fn_params {
for interior_ty in &types {
fcx.require_type_is_sized(
interior_ty.ty,
interior_ty.span,
ObligationCauseCode::SizedGeneratorInterior(generator_def_id),
);
}
}

// The types in the generator interior contain lifetimes local to the generator itself,
// which should not be exposed outside of the generator. Therefore, we replace these
// lifetimes with existentially-bound lifetimes, which reflect the exact value of the
Expand Down
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
4 changes: 4 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,10 @@ pub enum ObligationCauseCode<'tcx> {
SizedYieldType,
/// Inline asm operand type must be `Sized`.
InlineAsmSized,
/// Captured closure type must be `Sized`.
SizedClosureCapture(LocalDefId),
/// Types live across generator yields must be `Sized`.
SizedGeneratorInterior(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,24 @@ 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::SizedGeneratorInterior(generator_def_id) => {
let what = match self.tcx.generator_kind(generator_def_id) {
None | Some(hir::GeneratorKind::Gen) => "yield",
Some(hir::GeneratorKind::Async(..)) => "await",
};
err.note(format!("all values live across `{what}` must have a statically known size"));
}
ObligationCauseCode::ConstPatternStructural => {
err.note("constants used for pattern-matching must derive `PartialEq` and `Eq`");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/awaiting-unsized-param.rs:5:31
|
LL | #![feature(unsized_fn_params, unsized_locals)]
| ^^^^^^^^^^^^^^
|
= note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the size for values of type `(dyn Future<Output = T> + Unpin + 'static)` cannot be known at compilation time
--> $DIR/awaiting-unsized-param.rs:10:17
|
LL | async fn bug<T>(mut f: dyn Future<Output = T> + Unpin) -> T {
| ^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Future<Output = T> + Unpin + 'static)`
= note: all values captured by value by a closure must have a statically known size

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/awaiting-unsized-param.rs:5:31
|
LL | #![feature(unsized_fn_params, unsized_locals)]
| ^^^^^^^^^^^^^^
|
= note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the size for values of type `(dyn Future<Output = T> + Unpin + 'static)` cannot be known at compilation time
--> $DIR/awaiting-unsized-param.rs:10:17
|
LL | async fn bug<T>(mut f: dyn Future<Output = T> + Unpin) -> T {
| ^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn Future<Output = T> + Unpin + 'static)`
= note: all values captured by value by a closure must have a statically known size

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
15 changes: 15 additions & 0 deletions tests/ui/async-await/awaiting-unsized-param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// edition: 2021
// revisions: no_drop_tracking drop_tracking_mir
// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir

#![feature(unsized_fn_params, unsized_locals)]
//~^ WARN the feature `unsized_locals` is incomplete

use std::future::Future;

async fn bug<T>(mut f: dyn Future<Output = T> + Unpin) -> T {
//~^ ERROR the size for values of type `(dyn Future<Output = T> + Unpin + 'static)` cannot be known at compilation time
(&mut f).await
}

fn main() {}
21 changes: 21 additions & 0 deletions tests/ui/async-await/unsized-across-await.drop_tracking_mir.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/unsized-across-await.rs:5:12
|
LL | #![feature(unsized_locals)]
| ^^^^^^^^^^^^^^
|
= note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the size for values of type `dyn std::fmt::Display` cannot be known at compilation time
--> $DIR/unsized-across-await.rs:11:9
|
LL | let _x = *x;
| ^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn std::fmt::Display`
= note: all values live across `await` must have a statically known size

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
21 changes: 21 additions & 0 deletions tests/ui/async-await/unsized-across-await.no_drop_tracking.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/unsized-across-await.rs:5:12
|
LL | #![feature(unsized_locals)]
| ^^^^^^^^^^^^^^
|
= note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the size for values of type `dyn std::fmt::Display` cannot be known at compilation time
--> $DIR/unsized-across-await.rs:11:9
|
LL | let _x = *x;
| ^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `dyn std::fmt::Display`
= note: all values live across `await` must have a statically known size

error: aborting due to previous error; 1 warning emitted

For more information about this error, try `rustc --explain E0277`.
18 changes: 18 additions & 0 deletions tests/ui/async-await/unsized-across-await.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// edition: 2021
// revisions: no_drop_tracking drop_tracking_mir
// [drop_tracking_mir] compile-flags: -Zdrop-tracking-mir

#![feature(unsized_locals)]
//~^ WARN the feature `unsized_locals` is incomplete

async fn f() {}

async fn g(x: Box<dyn std::fmt::Display>) {
let _x = *x;
//~^ ERROR the size for values of type `dyn std::fmt::Display` cannot be known at compilation time
f().await;
}

fn main() {
let _a = g(Box::new(5));
}
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();
};
}
34 changes: 34 additions & 0 deletions tests/ui/generator/unsized-across-yield.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#![feature(generator_trait)]
#![feature(generators)]
#![feature(unsized_locals)]
//~^ WARN the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes

use std::ops::Generator;

fn across() -> impl Generator {
move || {
let b: [u8] = *(Box::new([]) as Box<[u8]>);
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time

yield;

for elem in b.iter() {}
}
}

fn capture() -> impl Generator {
let b: [u8] = *(Box::new([]) as Box<[u8]>);
move || {
println!("{:?}", &b);
//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time

yield;

for elem in b.iter() {}
}
}

fn main() {
across();
capture();
}
32 changes: 32 additions & 0 deletions tests/ui/generator/unsized-across-yield.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
warning: the feature `unsized_locals` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/unsized-across-yield.rs:3:12
|
LL | #![feature(unsized_locals)]
| ^^^^^^^^^^^^^^
|
= note: see issue #48055 <https://github.com/rust-lang/rust/issues/48055> for more information
= note: `#[warn(incomplete_features)]` on by default

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-across-yield.rs:10:13
|
LL | let b: [u8] = *(Box::new([]) as Box<[u8]>);
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
= note: all values live across `yield` must have a statically known size

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> $DIR/unsized-across-yield.rs:22:27
|
LL | move || {
| -- this closure captures all values by move
LL | println!("{:?}", &b);
| ^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `[u8]`
= note: all values captured by value by a closure must have a statically known size

error: aborting due to 2 previous errors; 1 warning emitted

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