Skip to content

Commit c4ddf6c

Browse files
authoredJul 25, 2022
Implement support for generators (rust-lang#1378)
1 parent a06136b commit c4ddf6c

36 files changed

+1800
-213
lines changed
 

‎kani-compiler/src/codegen_cprover_gotoc/codegen/function.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ impl<'tcx> GotocCtx<'tcx> {
4444
/// Get the number of parameters that the current function expects.
4545
fn get_params_size(&self) -> usize {
4646
let sig = self.current_fn().sig();
47-
let sig =
48-
self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig.unwrap());
47+
let sig = self.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), sig);
4948
// we don't call [codegen_function_sig] because we want to get a bit more metainformation.
5049
sig.inputs().len()
5150
}

‎kani-compiler/src/codegen_cprover_gotoc/codegen/intrinsic.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -313,9 +313,19 @@ impl<'tcx> GotocCtx<'tcx> {
313313
macro_rules! codegen_size_align {
314314
($which: ident) => {{
315315
let tp_ty = instance.substs.type_at(0);
316-
let arg = fargs.remove(0);
317-
let size_align = self.size_and_align_of_dst(tp_ty, arg);
318-
self.codegen_expr_to_place(p, size_align.$which)
316+
if tp_ty.is_generator() {
317+
let e = self.codegen_unimplemented(
318+
"size or alignment of a generator type",
319+
cbmc_ret_ty,
320+
loc,
321+
"https://github.com/model-checking/kani/issues/1395",
322+
);
323+
self.codegen_expr_to_place(p, e)
324+
} else {
325+
let arg = fargs.remove(0);
326+
let size_align = self.size_and_align_of_dst(tp_ty, arg);
327+
self.codegen_expr_to_place(p, size_align.$which)
328+
}
319329
}};
320330
}
321331

‎kani-compiler/src/codegen_cprover_gotoc/codegen/operand.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,7 @@ impl<'tcx> GotocCtx<'tcx> {
331331
let var = tcx.gen_function_local_variable(2, &func_name, cgt.clone()).to_expr();
332332
let body = vec![
333333
Stmt::decl(var.clone(), None, Location::none()),
334-
var.clone()
335-
.member("case", &tcx.symbol_table)
334+
tcx.codegen_discriminant_field(var.clone(), ty)
336335
.assign(param.to_expr(), Location::none()),
337336
var.ret(Location::none()),
338337
];
@@ -641,7 +640,7 @@ impl<'tcx> GotocCtx<'tcx> {
641640
/// This is tracked in <https://github.com/model-checking/kani/issues/1350>.
642641
pub fn codegen_func_symbol(&mut self, instance: Instance<'tcx>) -> (&Symbol, Type) {
643642
let func = self.symbol_name(instance);
644-
let funct = self.codegen_function_sig(self.fn_sig_of_instance(instance).unwrap());
643+
let funct = self.codegen_function_sig(self.fn_sig_of_instance(instance));
645644
// make sure the functions imported from other modules are in the symbol table
646645
let sym = self.ensure(&func, |ctx, _| {
647646
Symbol::function(

‎kani-compiler/src/codegen_cprover_gotoc/codegen/place.rs

+55-34
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_middle::{
1515
mir::{Field, Local, Place, ProjectionElem},
1616
ty::{self, Ty, TypeAndMut, VariantDef},
1717
};
18-
use rustc_target::abi::{TagEncoding, Variants};
18+
use rustc_target::abi::{TagEncoding, VariantIdx, Variants};
1919
use tracing::{debug, trace, warn};
2020

2121
/// A projection in Kani can either be to a type (the normal case),
@@ -24,6 +24,7 @@ use tracing::{debug, trace, warn};
2424
pub enum TypeOrVariant<'tcx> {
2525
Type(Ty<'tcx>),
2626
Variant(&'tcx VariantDef),
27+
GeneratorVariant(VariantIdx),
2728
}
2829

2930
/// A struct for storing the data for passing to `codegen_unimplemented`
@@ -129,7 +130,7 @@ impl<'tcx> ProjectedPlace<'tcx> {
129130
}
130131
}
131132
// TODO: handle Variant https://github.com/model-checking/kani/issues/448
132-
TypeOrVariant::Variant(_) => None,
133+
TypeOrVariant::Variant(_) | TypeOrVariant::GeneratorVariant(_) => None,
133134
}
134135
}
135136

@@ -197,7 +198,7 @@ impl<'tcx> TypeOrVariant<'tcx> {
197198
pub fn monomorphize(self, ctx: &GotocCtx<'tcx>) -> Self {
198199
match self {
199200
TypeOrVariant::Type(t) => TypeOrVariant::Type(ctx.monomorphize(t)),
200-
TypeOrVariant::Variant(_) => self,
201+
TypeOrVariant::Variant(_) | TypeOrVariant::GeneratorVariant(_) => self,
201202
}
202203
}
203204
}
@@ -207,6 +208,9 @@ impl<'tcx> TypeOrVariant<'tcx> {
207208
match self {
208209
TypeOrVariant::Type(t) => *t,
209210
TypeOrVariant::Variant(v) => panic!("expect a type but variant is found: {:?}", v),
211+
TypeOrVariant::GeneratorVariant(v) => {
212+
panic!("expect a type but generator variant is found: {:?}", v)
213+
}
210214
}
211215
}
212216

@@ -215,6 +219,9 @@ impl<'tcx> TypeOrVariant<'tcx> {
215219
match self {
216220
TypeOrVariant::Type(t) => panic!("expect a variant but type is found: {:?}", t),
217221
TypeOrVariant::Variant(v) => v,
222+
TypeOrVariant::GeneratorVariant(v) => {
223+
panic!("expect a variant but generator variant found {:?}", v)
224+
}
218225
}
219226
}
220227
}
@@ -269,12 +276,12 @@ impl<'tcx> GotocCtx<'tcx> {
269276
Ok(res.member(&field.name.to_string(), &self.symbol_table))
270277
}
271278
ty::Closure(..) => Ok(res.member(&f.index().to_string(), &self.symbol_table)),
272-
ty::Generator(..) => Err(UnimplementedData::new(
273-
"ty::Generator",
274-
"https://github.com/model-checking/kani/issues/416",
275-
Type::code(vec![], Type::empty()),
276-
*res.location(),
277-
)),
279+
ty::Generator(..) => {
280+
let field_name = self.generator_field_name(f.index().into());
281+
Ok(res
282+
.member("direct_fields", &self.symbol_table)
283+
.member(field_name, &self.symbol_table))
284+
}
278285
_ => unimplemented!(),
279286
}
280287
}
@@ -283,6 +290,10 @@ impl<'tcx> GotocCtx<'tcx> {
283290
let field = &v.fields[f.index()];
284291
Ok(res.member(&field.name.to_string(), &self.symbol_table))
285292
}
293+
TypeOrVariant::GeneratorVariant(_var_idx) => {
294+
let field_name = self.generator_field_name(f.index().into());
295+
Ok(res.member(field_name, &self.symbol_table))
296+
}
286297
}
287298
}
288299

@@ -498,33 +509,43 @@ impl<'tcx> GotocCtx<'tcx> {
498509
ProjectionElem::Downcast(_, idx) => {
499510
// downcast converts a variable of an enum type to one of its discriminated cases
500511
let t = before.mir_typ();
501-
match t.kind() {
512+
let (case_name, type_or_variant) = match t.kind() {
502513
ty::Adt(def, _) => {
503-
let variant = def.variants().get(idx).unwrap();
504-
let case_name = variant.name.to_string();
505-
let typ = TypeOrVariant::Variant(variant);
506-
let expr = match &self.layout_of(t).variants {
507-
Variants::Single { .. } => before.goto_expr,
508-
Variants::Multiple { tag_encoding, .. } => match tag_encoding {
509-
TagEncoding::Direct => before
510-
.goto_expr
511-
.member("cases", &self.symbol_table)
512-
.member(&case_name, &self.symbol_table),
513-
TagEncoding::Niche { .. } => {
514-
before.goto_expr.member(&case_name, &self.symbol_table)
515-
}
516-
},
517-
};
518-
ProjectedPlace::try_new(
519-
expr,
520-
typ,
521-
before.fat_ptr_goto_expr,
522-
before.fat_ptr_mir_typ,
523-
self,
524-
)
514+
let variant = def.variant(idx);
515+
(variant.name.as_str().into(), TypeOrVariant::Variant(variant))
525516
}
526-
_ => unreachable!("it's a bug to reach here!"),
527-
}
517+
ty::Generator(..) => {
518+
(self.generator_variant_name(idx), TypeOrVariant::GeneratorVariant(idx))
519+
}
520+
_ => unreachable!(
521+
"cannot downcast {:?} to a variant (only enums and generators can)",
522+
&t.kind()
523+
),
524+
};
525+
let layout = self.layout_of(t);
526+
let expr = match &layout.variants {
527+
Variants::Single { .. } => before.goto_expr,
528+
Variants::Multiple { tag_encoding, .. } => match tag_encoding {
529+
TagEncoding::Direct => {
530+
let cases = if t.is_generator() {
531+
before.goto_expr
532+
} else {
533+
before.goto_expr.member("cases", &self.symbol_table)
534+
};
535+
cases.member(case_name, &self.symbol_table)
536+
}
537+
TagEncoding::Niche { .. } => {
538+
before.goto_expr.member(case_name, &self.symbol_table)
539+
}
540+
},
541+
};
542+
ProjectedPlace::try_new(
543+
expr,
544+
type_or_variant,
545+
before.fat_ptr_goto_expr,
546+
before.fat_ptr_mir_typ,
547+
self,
548+
)
528549
}
529550
ProjectionElem::OpaqueCast(ty) => ProjectedPlace::try_new(
530551
before.goto_expr.cast_to(self.codegen_ty(ty)),

‎kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,25 @@ impl<'tcx> GotocCtx<'tcx> {
502502
}
503503
}
504504

505+
pub fn codegen_discriminant_field(&self, place: Expr, ty: Ty<'tcx>) -> Expr {
506+
let layout = self.layout_of(ty);
507+
assert!(
508+
matches!(
509+
&layout.variants,
510+
Variants::Multiple { tag_encoding: TagEncoding::Direct, .. }
511+
),
512+
"discriminant field (`case`) only exists for multiple variants and direct encoding"
513+
);
514+
let expr = if ty.is_generator() {
515+
// Generators are translated somewhat differently from enums (see [`GotoCtx::codegen_ty_generator`]).
516+
// As a consequence, the discriminant is accessed as `.direct_fields.case` instead of just `.case`.
517+
place.member("direct_fields", &self.symbol_table)
518+
} else {
519+
place
520+
};
521+
expr.member("case", &self.symbol_table)
522+
}
523+
505524
/// e: ty
506525
/// get the discriminant of e, of type res_ty
507526
pub fn codegen_get_discriminant(&mut self, e: Expr, ty: Ty<'tcx>, res_ty: Ty<'tcx>) -> Expr {
@@ -516,7 +535,7 @@ impl<'tcx> GotocCtx<'tcx> {
516535
}
517536
Variants::Multiple { tag, tag_encoding, .. } => match tag_encoding {
518537
TagEncoding::Direct => {
519-
e.member("case", &self.symbol_table).cast_to(self.codegen_ty(res_ty))
538+
self.codegen_discriminant_field(e, ty).cast_to(self.codegen_ty(res_ty))
520539
}
521540
TagEncoding::Niche { dataful_variant, niche_variants, niche_start } => {
522541
// This code follows the logic in the cranelift codegen backend:

‎kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs

+11-30
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ impl<'tcx> GotocCtx<'tcx> {
6565
let layout = self.layout_of(dst_mir_ty);
6666
if layout.is_zst() || dst_type.sizeof_in_bits(&self.symbol_table) == 0 {
6767
// We ignore assignment for all zero size types
68-
// Ignore generators too for now:
69-
// https://github.com/model-checking/kani/issues/416
7068
Stmt::skip(location)
7169
} else {
7270
unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(place))
@@ -77,26 +75,13 @@ impl<'tcx> GotocCtx<'tcx> {
7775
StatementKind::SetDiscriminant { place, variant_index } => {
7876
// this requires place points to an enum type.
7977
let pt = self.place_ty(place);
80-
let (def, _) = match pt.kind() {
81-
ty::Adt(def, substs) => (def, substs),
82-
ty::Generator(..) => {
83-
return self
84-
.codegen_unimplemented(
85-
"ty::Generator",
86-
Type::code(vec![], Type::empty()),
87-
location,
88-
"https://github.com/model-checking/kani/issues/416",
89-
)
90-
.as_stmt(location);
91-
}
92-
_ => unreachable!(),
93-
};
9478
let layout = self.layout_of(pt);
9579
match &layout.variants {
9680
Variants::Single { .. } => Stmt::skip(location),
9781
Variants::Multiple { tag, tag_encoding, .. } => match tag_encoding {
9882
TagEncoding::Direct => {
99-
let discr = def.discriminant_for_variant(self.tcx, *variant_index);
83+
let discr =
84+
pt.discriminant_for_variant(self.tcx, *variant_index).unwrap();
10085
let discr_t = self.codegen_enum_discr_typ(pt);
10186
// The constant created below may not fit into the type.
10287
// https://github.com/model-checking/kani/issues/996
@@ -111,13 +96,13 @@ impl<'tcx> GotocCtx<'tcx> {
11196
// DISCRIMINANT - val:0 ty:i8
11297
// DISCRIMINANT - val:1 ty:i8
11398
let discr = Expr::int_constant(discr.val, self.codegen_ty(discr_t));
114-
unwrap_or_return_codegen_unimplemented_stmt!(
99+
let place_goto_expr = unwrap_or_return_codegen_unimplemented_stmt!(
115100
self,
116101
self.codegen_place(place)
117102
)
118-
.goto_expr
119-
.member("case", &self.symbol_table)
120-
.assign(discr, location)
103+
.goto_expr;
104+
self.codegen_discriminant_field(place_goto_expr, pt)
105+
.assign(discr, location)
121106
}
122107
TagEncoding::Niche { dataful_variant, niche_variants, niche_start } => {
123108
if dataful_variant != variant_index {
@@ -206,7 +191,7 @@ impl<'tcx> GotocCtx<'tcx> {
206191
loc,
207192
),
208193
TerminatorKind::Return => {
209-
let rty = self.current_fn().sig().unwrap().skip_binder().output();
194+
let rty = self.current_fn().sig().skip_binder().output();
210195
if rty.is_unit() {
211196
self.codegen_ret_unit()
212197
} else {
@@ -534,15 +519,11 @@ impl<'tcx> GotocCtx<'tcx> {
534519
/// N.B. public only because instrinsics use this directly, too.
535520
pub(crate) fn codegen_funcall_args(&mut self, args: &[Operand<'tcx>]) -> Vec<Expr> {
536521
args.iter()
537-
.filter_map(|o| {
538-
let ot = self.operand_ty(o);
539-
if self.ignore_var_ty(ot) {
540-
trace!(operand=?o, typ=?ot, "codegen_funcall_args ignore");
541-
None
542-
} else if ot.is_bool() {
543-
Some(self.codegen_operand(o).cast_to(Type::c_bool()))
522+
.map(|o| {
523+
if self.operand_ty(o).is_bool() {
524+
self.codegen_operand(o).cast_to(Type::c_bool())
544525
} else {
545-
Some(self.codegen_operand(o))
526+
self.codegen_operand(o)
546527
}
547528
})
548529
.collect()

‎kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs

+277-102
Large diffs are not rendered by default.

‎kani-compiler/src/codegen_cprover_gotoc/context/current_fn.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub struct CurrentFnCtx<'tcx> {
2828
/// A human readable pretty name for the current function
2929
readable_name: String,
3030
/// The signature of the current function
31-
sig: Option<PolyFnSig<'tcx>>,
31+
sig: PolyFnSig<'tcx>,
3232
/// A counter to enable creating temporary variables
3333
temp_var_counter: u64,
3434
}
@@ -105,7 +105,7 @@ impl<'tcx> CurrentFnCtx<'tcx> {
105105
}
106106

107107
/// The signature of the function we are currently compiling
108-
pub fn sig(&self) -> Option<PolyFnSig<'tcx>> {
108+
pub fn sig(&self) -> PolyFnSig<'tcx> {
109109
self.sig
110110
}
111111
}

‎kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs

-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use crate::codegen_cprover_gotoc::codegen::PropertyClass;
66
use crate::codegen_cprover_gotoc::GotocCtx;
77
use cbmc::goto_program::{Expr, ExprValue, Location, Stmt, SymbolTable, Type};
88
use cbmc::{btree_string_map, InternedString};
9-
use rustc_errors::FatalError;
109
use rustc_middle::ty::layout::LayoutOf;
1110
use rustc_middle::ty::{Instance, Ty};
1211
use tracing::debug;
@@ -101,11 +100,6 @@ impl<'tcx> GotocCtx<'tcx> {
101100
}
102101
s
103102
}
104-
105-
pub fn emit_error_and_exit(&self, error_msg: &str) -> ! {
106-
self.tcx.sess.err(error_msg);
107-
FatalError.raise()
108-
}
109103
}
110104

111105
/// Members traverse path to get to the raw pointer of a box (b.0.pointer.pointer).
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
Status: FAILURE\
2-
Description: "ty::Generator is not currently supported by Kani. Please post your example at https://github.com/model-checking/kani/issues/416"
1+
VERIFICATION:- SUCCESSFUL

‎tests/expected/generators/as_parameter/main.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
// Test that includes a generator as a parameter to a function
5-
// Codegen should succeed, but verification should fail (codegen_unimplemented)
4+
// Test that a generator can be passed as a parameter.
5+
// (adapted from https://github.com/model-checking/kani/issues/1075)
66

77
#![feature(generators, generator_trait)]
88

9-
use std::ops::Generator;
9+
use std::ops::{Generator, GeneratorState};
10+
use std::pin::Pin;
1011

11-
fn foo<T>(g: T)
12+
fn foo<G: Generator<Yield = u8, Return = u8> + Unpin>(mut g: G)
1213
where
13-
T: Generator,
14+
<G as std::ops::Generator>::Return: std::cmp::PartialEq,
1415
{
15-
let _ = g;
16+
let res = Pin::new(&mut g).resume(());
17+
assert_eq!(res, GeneratorState::Yielded(1));
18+
let res2 = Pin::new(&mut g).resume(());
19+
assert_eq!(res2, GeneratorState::Complete(2));
1620
}
1721

1822
#[kani::proof]

‎tests/expected/generators/expected

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
SUCCESS\
2+
Description: "assertion failed: res == GeneratorState::Yielded(val)"
3+
4+
SUCCESS\
5+
Description: "assertion failed: res == GeneratorState::Complete(!val)"
6+
7+
UNREACHABLE\
8+
Description: "generator resumed after completion"
9+
10+
VERIFICATION:- SUCCESSFUL

‎tests/expected/generators/main.rs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0 OR MIT
3+
4+
#![feature(generators, generator_trait)]
5+
6+
use std::ops::{Generator, GeneratorState};
7+
use std::pin::Pin;
8+
9+
#[kani::proof]
10+
#[kani::unwind(2)]
11+
fn main() {
12+
let val: bool = kani::any();
13+
let mut generator = move || {
14+
let x = val;
15+
yield x;
16+
return !x;
17+
};
18+
19+
let res = Pin::new(&mut generator).resume(());
20+
assert_eq!(res, GeneratorState::Yielded(val));
21+
let res = Pin::new(&mut generator).resume(());
22+
assert_eq!(res, GeneratorState::Complete(!val));
23+
}
+13-2
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,13 @@
1-
The `generators` feature is not currently supported by Kani
2-
exited with status exit status: 101
1+
SUCCESS\
2+
Description: "unexpected return from resume"\
3+
in function main
4+
5+
SUCCESS\
6+
Description: "unexpected yield from resume"\
7+
in function main
8+
9+
UNREACHABLE\
10+
Description: "generator resumed after completion"
11+
in function main::{closure#0}
12+
13+
VERIFICATION:- SUCCESSFUL

‎tests/expected/generators/pin/main.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

44
// Test contains a call to a generator via a Pin
5-
// This is currently not supported, so Kani should error out
5+
// from https://github.com/model-checking/kani/issues/416
66

77
#![feature(generators, generator_trait)]
88

@@ -13,15 +13,15 @@ use std::pin::Pin;
1313
fn main() {
1414
let mut generator = || {
1515
yield 1;
16-
return "foo";
16+
return true;
1717
};
1818

1919
match Pin::new(&mut generator).resume(()) {
2020
GeneratorState::Yielded(1) => {}
2121
_ => panic!("unexpected return from resume"),
2222
}
2323
match Pin::new(&mut generator).resume(()) {
24-
GeneratorState::Complete("foo") => {}
25-
_ => panic!("unexpected return from resume"),
24+
GeneratorState::Complete(true) => {}
25+
_ => panic!("unexpected yield from resume"),
2626
}
2727
}

‎tests/kani/Generator/main.rs

+12-16
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,23 @@
11
// Copyright Kani Contributors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
// Check that we can codegen code that has a Generator type present,
5-
// as long as the path is not dynamically used. Full Generator support
6-
// tracked in: https://github.com/model-checking/kani/issues/416
4+
// This tests that generators work, even with a non-() resume type.
75

86
#![feature(generators, generator_trait)]
97

108
use std::ops::{Generator, GeneratorState};
11-
12-
// Seperate function to force translation
13-
fn maybe_call(call: bool) {
14-
if call {
15-
let mut _generator = || {
16-
yield 1;
17-
return 2;
18-
};
19-
} else {
20-
assert!(1 + 1 == 2);
21-
}
22-
}
9+
use std::pin::Pin;
2310

2411
#[kani::proof]
2512
fn main() {
26-
maybe_call(false);
13+
let mut add_one = |mut resume: u8| {
14+
loop {
15+
resume = yield resume.saturating_add(1);
16+
}
17+
};
18+
for _ in 0..2 {
19+
let val = kani::any();
20+
let res = Pin::new(&mut add_one).resume(val);
21+
assert_eq!(res, GeneratorState::Yielded(val.saturating_add(1)));
22+
}
2723
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/conditional-drop.rs
8+
9+
// run-pass
10+
11+
// revisions: default nomiropt
12+
//[nomiropt]compile-flags: -Z mir-opt-level=0
13+
14+
#![feature(generators, generator_trait)]
15+
16+
use std::ops::Generator;
17+
use std::pin::Pin;
18+
use std::sync::atomic::{AtomicUsize, Ordering};
19+
20+
static A: AtomicUsize = AtomicUsize::new(0);
21+
22+
struct B;
23+
24+
impl Drop for B {
25+
fn drop(&mut self) {
26+
A.fetch_add(1, Ordering::SeqCst);
27+
}
28+
}
29+
30+
fn test() -> bool {
31+
true
32+
}
33+
fn test2() -> bool {
34+
false
35+
}
36+
37+
#[kani::proof]
38+
#[kani::unwind(4)]
39+
fn main() {
40+
t1();
41+
t2();
42+
}
43+
44+
fn t1() {
45+
let mut a = || {
46+
let b = B;
47+
if test() {
48+
drop(b);
49+
}
50+
yield;
51+
};
52+
53+
let n = A.load(Ordering::SeqCst);
54+
Pin::new(&mut a).resume(());
55+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
56+
Pin::new(&mut a).resume(());
57+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
58+
}
59+
60+
fn t2() {
61+
let mut a = || {
62+
let b = B;
63+
if test2() {
64+
drop(b);
65+
}
66+
yield;
67+
};
68+
69+
let n = A.load(Ordering::SeqCst);
70+
Pin::new(&mut a).resume(());
71+
assert_eq!(A.load(Ordering::SeqCst), n);
72+
Pin::new(&mut a).resume(());
73+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/control-flow.rs
8+
9+
// run-pass
10+
11+
// revisions: default nomiropt
12+
//[nomiropt]compile-flags: -Z mir-opt-level=0
13+
14+
#![feature(generators, generator_trait)]
15+
16+
use std::marker::Unpin;
17+
use std::ops::{Generator, GeneratorState};
18+
use std::pin::Pin;
19+
20+
fn finish<T>(mut amt: usize, mut t: T) -> T::Return
21+
where
22+
T: Generator<(), Yield = ()> + Unpin,
23+
{
24+
loop {
25+
match Pin::new(&mut t).resume(()) {
26+
GeneratorState::Yielded(()) => amt = amt.checked_sub(1).unwrap(),
27+
GeneratorState::Complete(ret) => {
28+
assert_eq!(amt, 0);
29+
return ret;
30+
}
31+
}
32+
}
33+
}
34+
35+
#[kani::proof]
36+
#[kani::unwind(16)]
37+
fn main() {
38+
finish(1, || yield);
39+
finish(8, || {
40+
for _ in 0..8 {
41+
yield;
42+
}
43+
});
44+
finish(1, || {
45+
if true {
46+
yield;
47+
} else {
48+
}
49+
});
50+
finish(1, || {
51+
if false {
52+
} else {
53+
yield;
54+
}
55+
});
56+
finish(2, || {
57+
if {
58+
yield;
59+
false
60+
} {
61+
yield;
62+
panic!()
63+
}
64+
yield
65+
});
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/discriminant.rs
8+
9+
// This test creates some large generators with around 256 variants, some of them need a u16 discriminant instead of u8.
10+
// This test ensures that we use the right discriminant type.
11+
12+
//! Tests that generator discriminant sizes and ranges are chosen optimally and that they are
13+
//! reflected in the output of `mem::discriminant`.
14+
15+
// run-pass
16+
17+
#![feature(generators, generator_trait, core_intrinsics, discriminant_kind)]
18+
19+
use std::intrinsics::discriminant_value;
20+
use std::marker::{DiscriminantKind, Unpin};
21+
use std::mem::size_of_val;
22+
use std::{cmp, ops::*};
23+
24+
macro_rules! yield25 {
25+
($e:expr) => {
26+
yield $e;
27+
yield $e;
28+
yield $e;
29+
yield $e;
30+
yield $e;
31+
32+
yield $e;
33+
yield $e;
34+
yield $e;
35+
yield $e;
36+
yield $e;
37+
38+
yield $e;
39+
yield $e;
40+
yield $e;
41+
yield $e;
42+
yield $e;
43+
44+
yield $e;
45+
yield $e;
46+
yield $e;
47+
yield $e;
48+
yield $e;
49+
50+
yield $e;
51+
yield $e;
52+
yield $e;
53+
yield $e;
54+
yield $e;
55+
};
56+
}
57+
58+
/// Yields 250 times.
59+
macro_rules! yield250 {
60+
() => {
61+
yield250!(())
62+
};
63+
64+
($e:expr) => {
65+
yield25!($e);
66+
yield25!($e);
67+
yield25!($e);
68+
yield25!($e);
69+
yield25!($e);
70+
71+
yield25!($e);
72+
yield25!($e);
73+
yield25!($e);
74+
yield25!($e);
75+
yield25!($e);
76+
};
77+
}
78+
79+
fn cycle(
80+
gen: impl Generator<()> + Unpin + DiscriminantKind<Discriminant = u32>,
81+
expected_max_discr: u32,
82+
) {
83+
let mut gen = Box::pin(gen);
84+
let mut max_discr = 0;
85+
loop {
86+
max_discr = cmp::max(max_discr, discriminant_value(gen.as_mut().get_mut()));
87+
match gen.as_mut().resume(()) {
88+
GeneratorState::Yielded(_) => {}
89+
GeneratorState::Complete(_) => {
90+
assert_eq!(max_discr, expected_max_discr);
91+
return;
92+
}
93+
}
94+
}
95+
}
96+
97+
#[kani::proof]
98+
#[kani::unwind(260)]
99+
fn main() {
100+
// Has only one invalid discr. value.
101+
let gen_u8_tiny_niche = || {
102+
|| {
103+
// 3 reserved variants
104+
105+
yield250!(); // 253 variants
106+
107+
yield; // 254
108+
yield; // 255
109+
}
110+
};
111+
112+
// Uses all values in the u8 discriminant.
113+
let gen_u8_full = || {
114+
|| {
115+
// 3 reserved variants
116+
117+
yield250!(); // 253 variants
118+
119+
yield; // 254
120+
yield; // 255
121+
yield; // 256
122+
}
123+
};
124+
125+
// Barely needs a u16 discriminant.
126+
let gen_u16 = || {
127+
|| {
128+
// 3 reserved variants
129+
130+
yield250!(); // 253 variants
131+
132+
yield; // 254
133+
yield; // 255
134+
yield; // 256
135+
yield; // 257
136+
}
137+
};
138+
139+
// FIXME: size of generators does not work reliably (https://github.com/model-checking/kani/issues/1395)
140+
assert_eq!(size_of_val(&gen_u8_tiny_niche()), 1);
141+
assert_eq!(size_of_val(&Some(gen_u8_tiny_niche())), 1); // uses niche
142+
assert_eq!(size_of_val(&Some(Some(gen_u8_tiny_niche()))), 2); // cannot use niche anymore
143+
assert_eq!(size_of_val(&gen_u8_full()), 1);
144+
assert_eq!(size_of_val(&Some(gen_u8_full())), 2); // cannot use niche
145+
assert_eq!(size_of_val(&gen_u16()), 2);
146+
assert_eq!(size_of_val(&Some(gen_u16())), 2); // uses niche
147+
148+
cycle(gen_u8_tiny_niche(), 254);
149+
cycle(gen_u8_full(), 255);
150+
cycle(gen_u16(), 256);
151+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/env-drop.rs
8+
9+
// run-pass
10+
11+
// revisions: default nomiropt
12+
//[nomiropt]compile-flags: -Z mir-opt-level=0
13+
14+
#![feature(generators, generator_trait)]
15+
16+
use std::ops::Generator;
17+
use std::pin::Pin;
18+
use std::sync::atomic::{AtomicUsize, Ordering};
19+
20+
static A: AtomicUsize = AtomicUsize::new(0);
21+
22+
struct B;
23+
24+
impl Drop for B {
25+
fn drop(&mut self) {
26+
A.fetch_add(1, Ordering::SeqCst);
27+
}
28+
}
29+
30+
#[kani::proof]
31+
fn main() {
32+
t1();
33+
t2();
34+
t3();
35+
}
36+
37+
fn t1() {
38+
let b = B;
39+
let mut foo = || {
40+
yield;
41+
drop(b);
42+
};
43+
44+
let n = A.load(Ordering::SeqCst);
45+
drop(Pin::new(&mut foo).resume(()));
46+
assert_eq!(A.load(Ordering::SeqCst), n);
47+
drop(foo);
48+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
49+
}
50+
51+
fn t2() {
52+
let b = B;
53+
let mut foo = || {
54+
yield b;
55+
};
56+
57+
let n = A.load(Ordering::SeqCst);
58+
drop(Pin::new(&mut foo).resume(()));
59+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
60+
drop(foo);
61+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
62+
}
63+
64+
fn t3() {
65+
let b = B;
66+
let foo = || {
67+
yield;
68+
drop(b);
69+
};
70+
71+
let n = A.load(Ordering::SeqCst);
72+
assert_eq!(A.load(Ordering::SeqCst), n);
73+
drop(foo);
74+
assert_eq!(A.load(Ordering::SeqCst), n + 1);
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/iterator-count.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::marker::Unpin;
14+
use std::ops::{Generator, GeneratorState};
15+
use std::pin::Pin;
16+
17+
struct W<T>(T);
18+
19+
// This impl isn't safe in general, but the generator used in this test is movable
20+
// so it won't cause problems.
21+
impl<T: Generator<(), Return = ()> + Unpin> Iterator for W<T> {
22+
type Item = T::Yield;
23+
24+
fn next(&mut self) -> Option<Self::Item> {
25+
match Pin::new(&mut self.0).resume(()) {
26+
GeneratorState::Complete(..) => None,
27+
GeneratorState::Yielded(v) => Some(v),
28+
}
29+
}
30+
}
31+
32+
fn test() -> impl Generator<(), Return = (), Yield = u8> + Unpin {
33+
|| {
34+
for i in 1..6 {
35+
yield i
36+
}
37+
}
38+
}
39+
40+
#[kani::proof]
41+
#[kani::unwind(11)]
42+
fn main() {
43+
let end = 11;
44+
45+
let closure_test = |start| {
46+
move || {
47+
for i in start..end {
48+
yield i
49+
}
50+
}
51+
};
52+
53+
assert!(W(test()).chain(W(closure_test(6))).eq(1..11));
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/live-upvar-across-yield.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::ops::Generator;
14+
use std::pin::Pin;
15+
16+
#[kani::proof]
17+
#[kani::unwind(2)]
18+
fn main() {
19+
let b = |_| 3;
20+
let mut a = || {
21+
b(yield);
22+
};
23+
Pin::new(&mut a).resume(());
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/nested-generators.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::ops::{Generator, GeneratorState};
14+
use std::pin::Pin;
15+
16+
#[kani::proof]
17+
fn main() {
18+
let _generator = || {
19+
let mut sub_generator = || {
20+
yield 2;
21+
};
22+
23+
match Pin::new(&mut sub_generator).resume(()) {
24+
GeneratorState::Yielded(x) => {
25+
yield x;
26+
}
27+
_ => panic!(),
28+
};
29+
};
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/niche-in-generator.rs
8+
9+
// Test that niche finding works with captured generator upvars.
10+
11+
// run-pass
12+
13+
#![feature(generators, generator_trait)]
14+
15+
use std::ops::{Generator, GeneratorState};
16+
use std::pin::Pin;
17+
18+
use std::mem::size_of_val;
19+
20+
fn take<T>(_: T) {}
21+
22+
#[kani::proof]
23+
fn main() {
24+
let x = false;
25+
let mut gen1 = || {
26+
yield;
27+
take(x);
28+
};
29+
30+
// FIXME: for some reason, these asserts are very hard for CBMC to figure out
31+
// Kani didn't terminate within 5 minutes.
32+
// assert_eq!(Pin::new(&mut gen1).resume(()), GeneratorState::Yielded(()));
33+
// assert_eq!(Pin::new(&mut gen1).resume(()), GeneratorState::Complete(()));
34+
assert!(false); // to make the test fail without taking forever
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/niche-in-generator.rs
8+
9+
// Test that niche finding works with captured generator upvars.
10+
11+
// run-pass
12+
13+
#![feature(generators)]
14+
15+
use std::mem::size_of_val;
16+
17+
fn take<T>(_: T) {}
18+
19+
#[kani::proof]
20+
fn main() {
21+
let x = false;
22+
let gen1 = || {
23+
yield;
24+
take(x);
25+
};
26+
27+
// FIXME: size of generators does not work reliably (https://github.com/model-checking/kani/issues/1395)
28+
assert_eq!(size_of_val(&gen1), size_of_val(&Some(gen1)));
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/overlap-locals.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::ops::{Generator, GeneratorState};
14+
use std::pin::Pin;
15+
16+
#[kani::proof]
17+
fn main() {
18+
let mut a = || {
19+
{
20+
let w: i32 = 4;
21+
yield;
22+
}
23+
{
24+
let x: i32 = 5;
25+
yield;
26+
}
27+
{
28+
let y: i32 = 6;
29+
yield;
30+
}
31+
{
32+
let z: i32 = 7;
33+
yield;
34+
}
35+
};
36+
37+
assert_eq!(Pin::new(&mut a).resume(()), GeneratorState::Yielded(()));
38+
assert_eq!(Pin::new(&mut a).resume(()), GeneratorState::Yielded(()));
39+
assert_eq!(Pin::new(&mut a).resume(()), GeneratorState::Yielded(()));
40+
assert_eq!(Pin::new(&mut a).resume(()), GeneratorState::Yielded(()));
41+
assert_eq!(Pin::new(&mut a).resume(()), GeneratorState::Complete(()));
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/overlap-locals.rs
8+
9+
// run-pass
10+
11+
#![feature(generators)]
12+
13+
#[kani::proof]
14+
fn main() {
15+
let a = || {
16+
{
17+
let w: i32 = 4;
18+
yield;
19+
println!("{:?}", w);
20+
}
21+
{
22+
let x: i32 = 5;
23+
yield;
24+
println!("{:?}", x);
25+
}
26+
{
27+
let y: i32 = 6;
28+
yield;
29+
println!("{:?}", y);
30+
}
31+
{
32+
let z: i32 = 7;
33+
yield;
34+
println!("{:?}", z);
35+
}
36+
};
37+
38+
// FIXME: size of generators does not work reliably (https://github.com/model-checking/kani/issues/1395)
39+
assert_eq!(8, std::mem::size_of_val(&a));
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/resume-arg-size.rs
8+
9+
#![feature(generators)]
10+
11+
// run-pass
12+
13+
use std::mem::size_of_val;
14+
15+
#[kani::proof]
16+
fn main() {
17+
// Generator taking a `Copy`able resume arg.
18+
let gen_copy = |mut x: usize| {
19+
loop {
20+
drop(x);
21+
x = yield;
22+
}
23+
};
24+
25+
// Generator taking a non-`Copy` resume arg.
26+
let gen_move = |mut x: Box<usize>| {
27+
loop {
28+
drop(x);
29+
x = yield;
30+
}
31+
};
32+
33+
// Neither of these generators have the resume arg live across the `yield`, so they should be
34+
// 1 Byte in size (only storing the discriminant)
35+
// FIXME: size of generators does not work reliably (https://github.com/model-checking/kani/issues/1395)
36+
assert_eq!(size_of_val(&gen_copy), 1);
37+
assert_eq!(size_of_val(&gen_move), 1);
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/resume-arg-size.rs
8+
9+
#![feature(generators, generator_trait)]
10+
11+
use std::ops::{Generator, GeneratorState};
12+
use std::pin::Pin;
13+
14+
// run-pass
15+
16+
use std::mem::size_of_val;
17+
18+
#[kani::proof]
19+
fn main() {
20+
// Generator taking a `Copy`able resume arg.
21+
let mut gen_copy = |mut x: usize| {
22+
loop {
23+
drop(x);
24+
x = yield;
25+
}
26+
};
27+
28+
// Generator taking a non-`Copy` resume arg.
29+
let mut gen_move = |mut x: Box<usize>| {
30+
loop {
31+
drop(x);
32+
x = yield;
33+
}
34+
};
35+
36+
assert_eq!(Pin::new(&mut gen_copy).resume(0), GeneratorState::Yielded(()));
37+
assert_eq!(Pin::new(&mut gen_copy).resume(1), GeneratorState::Yielded(()));
38+
39+
assert_eq!(Pin::new(&mut gen_move).resume(Box::new(0)), GeneratorState::Yielded(()));
40+
assert_eq!(Pin::new(&mut gen_move).resume(Box::new(1)), GeneratorState::Yielded(()));
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/resume-live-across-yield.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::ops::{Generator, GeneratorState};
14+
use std::pin::Pin;
15+
use std::sync::atomic::{AtomicUsize, Ordering};
16+
17+
static DROP: AtomicUsize = AtomicUsize::new(0);
18+
19+
#[derive(PartialEq, Eq, Debug)]
20+
struct Dropper(String);
21+
22+
impl Drop for Dropper {
23+
fn drop(&mut self) {
24+
DROP.fetch_add(1, Ordering::SeqCst);
25+
}
26+
}
27+
28+
#[kani::proof]
29+
#[kani::unwind(16)]
30+
fn main() {
31+
let mut g = |mut _d| {
32+
_d = yield;
33+
_d
34+
};
35+
36+
let mut g = Pin::new(&mut g);
37+
38+
assert_eq!(
39+
g.as_mut().resume(Dropper(String::from("Hello world!"))),
40+
GeneratorState::Yielded(())
41+
);
42+
assert_eq!(DROP.load(Ordering::Acquire), 0);
43+
match g.as_mut().resume(Dropper(String::from("Number Two"))) {
44+
GeneratorState::Complete(dropper) => {
45+
assert_eq!(DROP.load(Ordering::Acquire), 1);
46+
assert_eq!(dropper.0, "Number Two");
47+
drop(dropper);
48+
assert_eq!(DROP.load(Ordering::Acquire), 2);
49+
}
50+
_ => unreachable!(),
51+
}
52+
53+
drop(g);
54+
assert_eq!(DROP.load(Ordering::Acquire), 2);
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/size-moved-locals.rs
8+
9+
// run-pass
10+
// Test that we don't duplicate storage for a variable that is moved to another
11+
// binding. This used to happen in the presence of unwind and drop edges (see
12+
// `complex` below.)
13+
//
14+
// The exact sizes here can change (we'd like to know when they do). What we
15+
// don't want to see is the `complex` generator size being upwards of 2048 bytes
16+
// (which would indicate it is reserving space for two copies of Foo.)
17+
//
18+
// See issue #59123 for a full explanation.
19+
20+
// edition:2018
21+
// ignore-wasm32 issue #62807
22+
// ignore-asmjs issue #62807
23+
24+
#![feature(generators, generator_trait)]
25+
26+
use std::ops::{Generator, GeneratorState};
27+
use std::pin::Pin;
28+
29+
const FOO_SIZE: usize = 1024;
30+
struct Foo([u8; FOO_SIZE]);
31+
32+
impl Drop for Foo {
33+
fn drop(&mut self) {}
34+
}
35+
36+
fn move_before_yield() -> impl Generator<Yield = (), Return = ()> {
37+
static || {
38+
let first = Foo([0; FOO_SIZE]);
39+
let _second = first;
40+
yield;
41+
// _second dropped here
42+
}
43+
}
44+
45+
fn noop() {}
46+
47+
fn move_before_yield_with_noop() -> impl Generator<Yield = (), Return = ()> {
48+
static || {
49+
let first = Foo([0; FOO_SIZE]);
50+
noop();
51+
let _second = first;
52+
yield;
53+
// _second dropped here
54+
}
55+
}
56+
57+
// Today we don't have NRVO (we allocate space for both `first` and `second`,)
58+
// but we can overlap `first` with `_third`.
59+
fn overlap_move_points() -> impl Generator<Yield = (), Return = ()> {
60+
static || {
61+
let first = Foo([0; FOO_SIZE]);
62+
yield;
63+
let second = first;
64+
yield;
65+
let _third = second;
66+
yield;
67+
}
68+
}
69+
70+
fn overlap_x_and_y() -> impl Generator<Yield = (), Return = ()> {
71+
static || {
72+
let x = Foo([0; FOO_SIZE]);
73+
yield;
74+
drop(x);
75+
let y = Foo([0; FOO_SIZE]);
76+
yield;
77+
drop(y);
78+
}
79+
}
80+
81+
#[kani::proof]
82+
fn main() {
83+
// FIXME: the following tests are very slow (did not terminate in a couple of minutes)
84+
/* let mut generator = move_before_yield();
85+
assert_eq!(
86+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
87+
GeneratorState::Yielded(())
88+
);
89+
assert_eq!(
90+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
91+
GeneratorState::Complete(())
92+
);
93+
94+
let mut generator = move_before_yield_with_noop();
95+
assert_eq!(
96+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
97+
GeneratorState::Yielded(())
98+
);
99+
assert_eq!(
100+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
101+
GeneratorState::Complete(())
102+
);
103+
104+
let mut generator = overlap_move_points();
105+
assert_eq!(
106+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
107+
GeneratorState::Yielded(())
108+
);
109+
assert_eq!(
110+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
111+
GeneratorState::Yielded(())
112+
);
113+
assert_eq!(
114+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
115+
GeneratorState::Yielded(())
116+
);
117+
assert_eq!(
118+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
119+
GeneratorState::Complete(())
120+
);
121+
122+
let mut generator = overlap_x_and_y();
123+
assert_eq!(
124+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
125+
GeneratorState::Yielded(())
126+
);
127+
assert_eq!(
128+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
129+
GeneratorState::Yielded(())
130+
);
131+
assert_eq!(
132+
unsafe { Pin::new_unchecked(&mut generator) }.resume(()),
133+
GeneratorState::Complete(())
134+
); */
135+
assert!(false); // to make the test fail without taking forever
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/size-moved-locals.rs
8+
9+
// run-pass
10+
// Test that we don't duplicate storage for a variable that is moved to another
11+
// binding. This used to happen in the presence of unwind and drop edges (see
12+
// `complex` below.)
13+
//
14+
// The exact sizes here can change (we'd like to know when they do). What we
15+
// don't want to see is the `complex` generator size being upwards of 2048 bytes
16+
// (which would indicate it is reserving space for two copies of Foo.)
17+
//
18+
// See issue #59123 for a full explanation.
19+
20+
// edition:2018
21+
// ignore-wasm32 issue #62807
22+
// ignore-asmjs issue #62807
23+
24+
#![feature(generators, generator_trait)]
25+
26+
use std::ops::Generator;
27+
28+
const FOO_SIZE: usize = 1024;
29+
struct Foo([u8; FOO_SIZE]);
30+
31+
impl Drop for Foo {
32+
fn drop(&mut self) {}
33+
}
34+
35+
fn move_before_yield() -> impl Generator<Yield = (), Return = ()> {
36+
static || {
37+
let first = Foo([0; FOO_SIZE]);
38+
let _second = first;
39+
yield;
40+
// _second dropped here
41+
}
42+
}
43+
44+
fn noop() {}
45+
46+
fn move_before_yield_with_noop() -> impl Generator<Yield = (), Return = ()> {
47+
static || {
48+
let first = Foo([0; FOO_SIZE]);
49+
noop();
50+
let _second = first;
51+
yield;
52+
// _second dropped here
53+
}
54+
}
55+
56+
// Today we don't have NRVO (we allocate space for both `first` and `second`,)
57+
// but we can overlap `first` with `_third`.
58+
fn overlap_move_points() -> impl Generator<Yield = (), Return = ()> {
59+
static || {
60+
let first = Foo([0; FOO_SIZE]);
61+
yield;
62+
let second = first;
63+
yield;
64+
let _third = second;
65+
yield;
66+
}
67+
}
68+
69+
fn overlap_x_and_y() -> impl Generator<Yield = (), Return = ()> {
70+
static || {
71+
let x = Foo([0; FOO_SIZE]);
72+
yield;
73+
drop(x);
74+
let y = Foo([0; FOO_SIZE]);
75+
yield;
76+
drop(y);
77+
}
78+
}
79+
80+
#[kani::proof]
81+
fn main() {
82+
// FIXME: size of generators does not work reliably (https://github.com/model-checking/kani/issues/1395)
83+
assert_eq!(1025, std::mem::size_of_val(&move_before_yield()));
84+
// The following assertion fails for some reason (tracking issue: https://github.com/model-checking/kani/issues/1395).
85+
// But it also fails for WASM (https://github.com/rust-lang/rust/issues/62807),
86+
// so it is probably not a big problem:
87+
assert_eq!(1026, std::mem::size_of_val(&move_before_yield_with_noop()));
88+
assert_eq!(2051, std::mem::size_of_val(&overlap_move_points()));
89+
assert_eq!(1026, std::mem::size_of_val(&overlap_x_and_y()));
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/smoke-resume-args.rs
8+
9+
// run-pass
10+
11+
// revisions: default nomiropt
12+
//[nomiropt]compile-flags: -Z mir-opt-level=0
13+
14+
#![feature(generators, generator_trait)]
15+
16+
use std::fmt::Debug;
17+
use std::marker::Unpin;
18+
use std::ops::{
19+
Generator,
20+
GeneratorState::{self, *},
21+
};
22+
use std::pin::Pin;
23+
use std::sync::atomic::{AtomicUsize, Ordering};
24+
25+
fn drain<G: Generator<R, Yield = Y> + Unpin, R, Y>(
26+
gen: &mut G,
27+
inout: Vec<(R, GeneratorState<Y, G::Return>)>,
28+
) where
29+
Y: Debug + PartialEq,
30+
G::Return: Debug + PartialEq,
31+
{
32+
let mut gen = Pin::new(gen);
33+
34+
for (input, out) in inout {
35+
assert_eq!(gen.as_mut().resume(input), out);
36+
}
37+
}
38+
39+
static DROPS: AtomicUsize = AtomicUsize::new(0);
40+
41+
#[derive(Debug, PartialEq)]
42+
struct DropMe;
43+
44+
impl Drop for DropMe {
45+
fn drop(&mut self) {
46+
DROPS.fetch_add(1, Ordering::SeqCst);
47+
}
48+
}
49+
50+
fn expect_drops<T>(expected_drops: usize, f: impl FnOnce() -> T) -> T {
51+
DROPS.store(0, Ordering::SeqCst);
52+
53+
let res = f();
54+
55+
let actual_drops = DROPS.load(Ordering::SeqCst);
56+
assert_eq!(actual_drops, expected_drops);
57+
res
58+
}
59+
60+
#[kani::proof]
61+
#[kani::unwind(8)]
62+
fn main() {
63+
drain(
64+
&mut |mut b| {
65+
while b != 0 {
66+
b = yield (b + 1);
67+
}
68+
-1
69+
},
70+
vec![(1, Yielded(2)), (-45, Yielded(-44)), (500, Yielded(501)), (0, Complete(-1))],
71+
);
72+
73+
expect_drops(2, || drain(&mut |a| yield a, vec![(DropMe, Yielded(DropMe))]));
74+
75+
expect_drops(6, || {
76+
drain(
77+
&mut |a| yield yield a,
78+
vec![(DropMe, Yielded(DropMe)), (DropMe, Yielded(DropMe)), (DropMe, Complete(DropMe))],
79+
)
80+
});
81+
82+
#[allow(unreachable_code)]
83+
expect_drops(2, || drain(&mut |a| yield return a, vec![(DropMe, Complete(DropMe))]));
84+
85+
expect_drops(2, || {
86+
drain(
87+
&mut |a: DropMe| {
88+
if false { yield () } else { a }
89+
},
90+
vec![(DropMe, Complete(DropMe))],
91+
)
92+
});
93+
94+
expect_drops(4, || {
95+
drain(
96+
#[allow(unused_assignments, unused_variables)]
97+
&mut |mut a: DropMe| {
98+
a = yield;
99+
a = yield;
100+
a = yield;
101+
},
102+
vec![
103+
(DropMe, Yielded(())),
104+
(DropMe, Yielded(())),
105+
(DropMe, Yielded(())),
106+
(DropMe, Complete(())),
107+
],
108+
)
109+
});
110+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/smoke.rs
8+
9+
// run-pass
10+
11+
// revisions: default nomiropt
12+
//[nomiropt]compile-flags: -Z mir-opt-level=0
13+
14+
// ignore-emscripten no threads support
15+
// compile-flags: --test
16+
17+
#![feature(generators, generator_trait)]
18+
19+
use std::ops::{Generator, GeneratorState};
20+
use std::pin::Pin;
21+
use std::thread;
22+
23+
#[kani::proof]
24+
fn simple() {
25+
let mut foo = || {
26+
if false {
27+
yield;
28+
}
29+
};
30+
31+
match Pin::new(&mut foo).resume(()) {
32+
GeneratorState::Complete(()) => {}
33+
s => panic!("bad state: {:?}", s),
34+
}
35+
}
36+
37+
#[kani::proof]
38+
#[kani::unwind(4)]
39+
fn return_capture() {
40+
let a = String::from("foo");
41+
let mut foo = || {
42+
if false {
43+
yield;
44+
}
45+
a
46+
};
47+
48+
match Pin::new(&mut foo).resume(()) {
49+
GeneratorState::Complete(ref s) if *s == "foo" => {}
50+
s => panic!("bad state: {:?}", s),
51+
}
52+
}
53+
54+
#[kani::proof]
55+
fn simple_yield() {
56+
let mut foo = || {
57+
yield;
58+
};
59+
60+
match Pin::new(&mut foo).resume(()) {
61+
GeneratorState::Yielded(()) => {}
62+
s => panic!("bad state: {:?}", s),
63+
}
64+
match Pin::new(&mut foo).resume(()) {
65+
GeneratorState::Complete(()) => {}
66+
s => panic!("bad state: {:?}", s),
67+
}
68+
}
69+
70+
#[kani::proof]
71+
#[kani::unwind(4)]
72+
fn yield_capture() {
73+
let b = String::from("foo");
74+
let mut foo = || {
75+
yield b;
76+
};
77+
78+
match Pin::new(&mut foo).resume(()) {
79+
GeneratorState::Yielded(ref s) if *s == "foo" => {}
80+
s => panic!("bad state: {:?}", s),
81+
}
82+
match Pin::new(&mut foo).resume(()) {
83+
GeneratorState::Complete(()) => {}
84+
s => panic!("bad state: {:?}", s),
85+
}
86+
}
87+
88+
#[kani::proof]
89+
#[kani::unwind(4)]
90+
fn simple_yield_value() {
91+
let mut foo = || {
92+
yield String::from("bar");
93+
return String::from("foo");
94+
};
95+
96+
match Pin::new(&mut foo).resume(()) {
97+
GeneratorState::Yielded(ref s) if *s == "bar" => {}
98+
s => panic!("bad state: {:?}", s),
99+
}
100+
match Pin::new(&mut foo).resume(()) {
101+
GeneratorState::Complete(ref s) if *s == "foo" => {}
102+
s => panic!("bad state: {:?}", s),
103+
}
104+
}
105+
106+
#[kani::proof]
107+
#[kani::unwind(4)]
108+
fn return_after_yield() {
109+
let a = String::from("foo");
110+
let mut foo = || {
111+
yield;
112+
return a;
113+
};
114+
115+
match Pin::new(&mut foo).resume(()) {
116+
GeneratorState::Yielded(()) => {}
117+
s => panic!("bad state: {:?}", s),
118+
}
119+
match Pin::new(&mut foo).resume(()) {
120+
GeneratorState::Complete(ref s) if *s == "foo" => {}
121+
s => panic!("bad state: {:?}", s),
122+
}
123+
}
124+
125+
// This test is useless for Kani
126+
fn send_and_sync() {
127+
assert_send_sync(|| yield);
128+
assert_send_sync(|| {
129+
yield String::from("foo");
130+
});
131+
assert_send_sync(|| {
132+
yield;
133+
return String::from("foo");
134+
});
135+
let a = 3;
136+
assert_send_sync(|| {
137+
yield a;
138+
return;
139+
});
140+
let a = 3;
141+
assert_send_sync(move || {
142+
yield a;
143+
return;
144+
});
145+
let a = String::from("a");
146+
assert_send_sync(|| {
147+
yield;
148+
drop(a);
149+
return;
150+
});
151+
let a = String::from("a");
152+
assert_send_sync(move || {
153+
yield;
154+
drop(a);
155+
return;
156+
});
157+
158+
fn assert_send_sync<T: Send + Sync>(_: T) {}
159+
}
160+
161+
// Kani does not support threads, so we cannot run this test:
162+
fn send_over_threads() {
163+
let mut foo = || yield;
164+
thread::spawn(move || {
165+
match Pin::new(&mut foo).resume(()) {
166+
GeneratorState::Yielded(()) => {}
167+
s => panic!("bad state: {:?}", s),
168+
}
169+
match Pin::new(&mut foo).resume(()) {
170+
GeneratorState::Complete(()) => {}
171+
s => panic!("bad state: {:?}", s),
172+
}
173+
})
174+
.join()
175+
.unwrap();
176+
177+
let a = String::from("a");
178+
let mut foo = || yield a;
179+
thread::spawn(move || {
180+
match Pin::new(&mut foo).resume(()) {
181+
GeneratorState::Yielded(ref s) if *s == "a" => {}
182+
s => panic!("bad state: {:?}", s),
183+
}
184+
match Pin::new(&mut foo).resume(()) {
185+
GeneratorState::Complete(()) => {}
186+
s => panic!("bad state: {:?}", s),
187+
}
188+
})
189+
.join()
190+
.unwrap();
191+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/static-generator.rs
8+
9+
// run-pass
10+
11+
#![feature(generators, generator_trait)]
12+
13+
use std::ops::{Generator, GeneratorState};
14+
use std::pin::Pin;
15+
16+
#[kani::proof]
17+
#[kani::unwind(2)]
18+
fn main() {
19+
let mut generator = static || {
20+
let a = true;
21+
let b = &a;
22+
yield;
23+
assert_eq!(b as *const _, &a as *const _);
24+
};
25+
// SAFETY: We shadow the original generator variable so have no safe API to
26+
// move it after this point.
27+
let mut generator = unsafe { Pin::new_unchecked(&mut generator) };
28+
assert_eq!(generator.as_mut().resume(()), GeneratorState::Yielded(()));
29+
assert_eq!(generator.as_mut().resume(()), GeneratorState::Complete(()));
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: Apache-2.0 OR MIT
2+
//
3+
// Modifications Copyright Kani Contributors
4+
// See GitHub history for details.
5+
6+
// Copyright rustc Contributors
7+
// Adapted from rustc: https://github.com/rust-lang/rust/tree/5f98537eb7b5f42c246a52c550813c3cff336069/src/test/ui/generator/yield-in-box.rs
8+
9+
// run-pass
10+
// Test that box-statements with yields in them work.
11+
12+
#![feature(generators, box_syntax, generator_trait)]
13+
use std::ops::Generator;
14+
use std::ops::GeneratorState;
15+
use std::pin::Pin;
16+
17+
#[kani::proof]
18+
#[kani::unwind(2)]
19+
fn main() {
20+
let x = 0i32;
21+
|| {
22+
//~ WARN unused generator that must be used
23+
let y = 2u32;
24+
{
25+
let _t = box (&x, yield 0, &y);
26+
}
27+
match box (&x, yield 0, &y) {
28+
_t => {}
29+
}
30+
};
31+
32+
let mut g = |_| box yield;
33+
assert_eq!(Pin::new(&mut g).resume(1), GeneratorState::Yielded(()));
34+
assert_eq!(Pin::new(&mut g).resume(2), GeneratorState::Complete(box 2));
35+
}

0 commit comments

Comments
 (0)
Please sign in to comment.