Skip to content

Commit e1d8541

Browse files
authored
add warning (#17647)
1 parent e440a16 commit e1d8541

File tree

15 files changed

+491
-4
lines changed

15 files changed

+491
-4
lines changed

third_party/move/move-compiler-v2/src/env_pipeline/closure_checker.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use move_core_types::ability::Ability;
1717
use move_model::{
1818
ast::{ExpData, Operation},
1919
model::GlobalEnv,
20+
ty::Type,
2021
well_known,
2122
};
2223

@@ -90,6 +91,12 @@ pub fn check_closures(env: &GlobalEnv) {
9091
};
9192
for captured in args {
9293
let captured_ty = env.get_node_type(captured.node_id());
94+
// when capturing a value that contains option, we need to generate a warning
95+
// After refactoring option type to use enum, we can lift this limitation
96+
// TODO: remove it after option type is refactored to use enum
97+
if contains_option_type(env, &captured_ty) {
98+
env.warning(&env.get_node_loc(captured.node_id()), "capturing option values is currently not supported");
99+
}
93100
if captured_ty.is_reference() {
94101
env.error(
95102
&env.get_node_loc(captured.node_id()),
@@ -135,3 +142,49 @@ pub fn check_closures(env: &GlobalEnv) {
135142
}
136143
}
137144
}
145+
146+
/// Check if the type contains an option type
147+
/// Note that this function does not find contained options within reference types/function types
148+
/// because it is used for checking captured values only
149+
fn contains_option_type(env: &GlobalEnv, ty: &Type) -> bool {
150+
match ty {
151+
Type::Vector(ty) => contains_option_type(env, ty),
152+
Type::Struct(mid, sid, tys) => {
153+
let struct_env = env.get_module(*mid).into_struct(*sid);
154+
if struct_env.is_option_type() {
155+
return true;
156+
}
157+
if struct_env.has_variants() {
158+
for variant in struct_env.get_variants() {
159+
for field in struct_env.get_fields_of_variant(variant) {
160+
if contains_option_type(env, &field.get_type()) {
161+
return true;
162+
}
163+
}
164+
}
165+
} else {
166+
for field in struct_env.get_fields() {
167+
if contains_option_type(env, &field.get_type()) {
168+
return true;
169+
}
170+
}
171+
}
172+
tys.iter()
173+
.zip(struct_env.get_type_parameters().iter())
174+
.filter(|(_, param)| !param.1.is_phantom)
175+
.any(|(t, _)| contains_option_type(env, t))
176+
},
177+
// since fun params and result does not appear in layout, we can safely return false,
178+
Type::Fun(..) => false,
179+
Type::Primitive(..) => false,
180+
// compiler error will be generated separately
181+
// we are looking at option values that are captured, and because references cannot be captured,
182+
// we do not have to recurse here.
183+
Type::Reference(..) => false,
184+
Type::TypeParameter(..) => false,
185+
_ => unreachable!(
186+
"ICE: argument with type `{}` should not appear in this context",
187+
ty.display(&env.get_type_display_ctx())
188+
),
189+
}
190+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
2+
Diagnostics:
3+
warning: capturing option values is currently not supported
4+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_1.move:11:66
5+
6+
11 │ let f: ||option::Option<u64> has copy+drop+store = || id(v);
7+
│ ^
8+
9+
warning: capturing option values is currently not supported
10+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_1.move:16:67
11+
12+
16 │ let _f: ||option::Option<u64> has copy+drop+store = || id(v);
13+
│ ^
14+
15+
// -- Model dump before first bytecode pipeline
16+
module 0x99::m {
17+
use std::option;
18+
struct FunctionStore {
19+
f: ||0x1::option::Option<u64> has copy + drop + store,
20+
}
21+
private entry fun entry_func() {
22+
{
23+
let v: 0x1::option::Option<u64> = option::none<u64>();
24+
{
25+
let _f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id(v);
26+
Tuple()
27+
}
28+
}
29+
}
30+
public fun id(x: 0x1::option::Option<u64>): 0x1::option::Option<u64> {
31+
x
32+
}
33+
private fun init_module(account: &signer) {
34+
{
35+
let v: 0x1::option::Option<u64> = option::none<u64>();
36+
{
37+
let f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id(v);
38+
MoveTo<FunctionStore>(account, pack m::FunctionStore(f));
39+
Tuple()
40+
}
41+
}
42+
}
43+
} // end 0x99::m
44+
45+
// -- Sourcified model before first bytecode pipeline
46+
module 0x99::m {
47+
use std::option;
48+
struct FunctionStore has key {
49+
f: ||0x1::option::Option<u64> has copy + drop + store,
50+
}
51+
entry fun entry_func() {
52+
let v = 0x1::option::none<u64>();
53+
let _f: ||0x1::option::Option<u64> has copy + drop + store = || id(v);
54+
}
55+
public fun id(x: 0x1::option::Option<u64>): 0x1::option::Option<u64> {
56+
x
57+
}
58+
fun init_module(account: &signer) {
59+
let v = 0x1::option::none<u64>();
60+
let f: ||0x1::option::Option<u64> has copy + drop + store = || id(v);
61+
move_to<FunctionStore>(FunctionStore{f: f}, account);
62+
}
63+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module 0x99::m {
2+
use std::option;
3+
struct FunctionStore has key {
4+
f: ||option::Option<u64> has copy+drop+store,
5+
}
6+
public fun id(x: option::Option<u64>): option::Option<u64> {
7+
x
8+
}
9+
fun init_module(account: &signer) {
10+
let v = option::none();
11+
let f: ||option::Option<u64> has copy+drop+store = || id(v);
12+
move_to(account, FunctionStore { f });
13+
}
14+
entry fun entry_func() {
15+
let v = option::none();
16+
let _f: ||option::Option<u64> has copy+drop+store = || id(v);
17+
}
18+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
2+
Diagnostics:
3+
warning: capturing option values is currently not supported
4+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_2.move:14:67
5+
6+
14 │ let _f: ||option::Option<u64> has copy+drop+store = || id(v);
7+
│ ^
8+
9+
// -- Model dump before first bytecode pipeline
10+
module 0x99::m {
11+
use std::option;
12+
struct FunctionStore {
13+
f: ||0x1::option::Option<u64> has copy + drop + store,
14+
}
15+
struct OptionStore {
16+
o: 0x1::option::Option<u64>,
17+
}
18+
private entry fun entry_func() {
19+
{
20+
let v: OptionStore = pack m::OptionStore(option::none<u64>());
21+
{
22+
let _f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id(v);
23+
Tuple()
24+
}
25+
}
26+
}
27+
public fun id(x: OptionStore): 0x1::option::Option<u64> {
28+
select m::OptionStore.o<OptionStore>(x)
29+
}
30+
} // end 0x99::m
31+
32+
// -- Sourcified model before first bytecode pipeline
33+
module 0x99::m {
34+
use std::option;
35+
struct FunctionStore has key {
36+
f: ||0x1::option::Option<u64> has copy + drop + store,
37+
}
38+
struct OptionStore has copy, drop, store {
39+
o: 0x1::option::Option<u64>,
40+
}
41+
entry fun entry_func() {
42+
let v = OptionStore{o: 0x1::option::none<u64>()};
43+
let _f: ||0x1::option::Option<u64> has copy + drop + store = || id(v);
44+
}
45+
public fun id(x: OptionStore): 0x1::option::Option<u64> {
46+
x.o
47+
}
48+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
module 0x99::m {
2+
use std::option;
3+
struct OptionStore has copy,drop,store {
4+
o: option::Option<u64>,
5+
}
6+
struct FunctionStore has key {
7+
f: ||option::Option<u64> has copy+drop+store,
8+
}
9+
public fun id(x: OptionStore): option::Option<u64> {
10+
x.o
11+
}
12+
entry fun entry_func() {
13+
let v = OptionStore { o: option::none() };
14+
let _f: ||option::Option<u64> has copy+drop+store = || id(v);
15+
}
16+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
2+
Diagnostics:
3+
warning: capturing option values is currently not supported
4+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_3.move:12:67
5+
6+
12 │ let _f: ||option::Option<u64> has copy+drop+store = || id(v);
7+
│ ^
8+
9+
// -- Model dump before first bytecode pipeline
10+
module 0x99::m {
11+
use std::option;
12+
use std::vector;
13+
struct FunctionStore {
14+
f: ||0x1::option::Option<u64> has copy + drop + store,
15+
}
16+
private entry fun entry_func() {
17+
{
18+
let v: vector<0x1::option::Option<u64>> = vector::empty<0x1::option::Option<u64>>();
19+
{
20+
let _f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id(v);
21+
Tuple()
22+
}
23+
}
24+
}
25+
public fun id(_x: vector<0x1::option::Option<u64>>): 0x1::option::Option<u64> {
26+
option::none<u64>()
27+
}
28+
} // end 0x99::m
29+
30+
// -- Sourcified model before first bytecode pipeline
31+
module 0x99::m {
32+
use std::option;
33+
use std::vector;
34+
struct FunctionStore has key {
35+
f: ||0x1::option::Option<u64> has copy + drop + store,
36+
}
37+
entry fun entry_func() {
38+
let v = 0x1::vector::empty<0x1::option::Option<u64>>();
39+
let _f: ||0x1::option::Option<u64> has copy + drop + store = || id(v);
40+
}
41+
public fun id(_x: vector<0x1::option::Option<u64>>): 0x1::option::Option<u64> {
42+
0x1::option::none<u64>()
43+
}
44+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module 0x99::m {
2+
use std::option;
3+
use std::vector;
4+
struct FunctionStore has key {
5+
f: ||option::Option<u64> has copy+drop+store,
6+
}
7+
public fun id(_x: vector<option::Option<u64>>): option::Option<u64> {
8+
option::none()
9+
}
10+
entry fun entry_func() {
11+
let v = vector::empty();
12+
let _f: ||option::Option<u64> has copy+drop+store = || id(v);
13+
}
14+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
2+
Diagnostics:
3+
warning: capturing option values is currently not supported
4+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_4.move:14:66
5+
6+
14 │ let f: ||option::Option<u64> has copy+drop+store = || id(v);
7+
│ ^
8+
9+
warning: capturing option values is currently not supported
10+
┌─ tests/checking-lang-v2.2/lambda/capturing_option_4.move:19:67
11+
12+
19 │ let _f: ||option::Option<u64> has copy+drop+store = || id(v);
13+
│ ^
14+
15+
// -- Model dump before first bytecode pipeline
16+
module 0x99::m {
17+
use std::option;
18+
struct FunctionStore {
19+
f: ||0x1::option::Option<u64> has copy + drop + store,
20+
}
21+
struct Store<T> {
22+
o: T,
23+
}
24+
private entry fun entry_func() {
25+
{
26+
let v: Store<0x1::option::Option<u64>> = pack m::Store<0x1::option::Option<u64>>(option::none<u64>());
27+
{
28+
let _f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id<0x1::option::Option<u64>>(v);
29+
Tuple()
30+
}
31+
}
32+
}
33+
public fun id<T>(x: Store<T>): T {
34+
select m::Store.o<Store<T>>(x)
35+
}
36+
private fun init_module(account: &signer) {
37+
{
38+
let v: Store<0x1::option::Option<u64>> = pack m::Store<0x1::option::Option<u64>>(option::none<u64>());
39+
{
40+
let f: ||0x1::option::Option<u64> has copy + drop + store = closure#1m::id<0x1::option::Option<u64>>(v);
41+
MoveTo<FunctionStore>(account, pack m::FunctionStore(f));
42+
Tuple()
43+
}
44+
}
45+
}
46+
} // end 0x99::m
47+
48+
// -- Sourcified model before first bytecode pipeline
49+
module 0x99::m {
50+
use std::option;
51+
struct FunctionStore has key {
52+
f: ||0x1::option::Option<u64> has copy + drop + store,
53+
}
54+
struct Store<T: copy + drop + store> has copy, drop, store {
55+
o: T,
56+
}
57+
entry fun entry_func() {
58+
let v = Store<0x1::option::Option<u64>>{o: 0x1::option::none<u64>()};
59+
let _f: ||0x1::option::Option<u64> has copy + drop + store = || id<0x1::option::Option<u64>>(v);
60+
}
61+
public fun id<T: copy + drop + store>(x: Store<T>): T {
62+
x.o
63+
}
64+
fun init_module(account: &signer) {
65+
let v = Store<0x1::option::Option<u64>>{o: 0x1::option::none<u64>()};
66+
let f: ||0x1::option::Option<u64> has copy + drop + store = || id<0x1::option::Option<u64>>(v);
67+
move_to<FunctionStore>(FunctionStore{f: f}, account);
68+
}
69+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module 0x99::m {
2+
use std::option;
3+
struct Store<T: store+drop+copy> has copy,drop,store {
4+
o: T,
5+
}
6+
struct FunctionStore has key {
7+
f: ||option::Option<u64> has copy+drop+store,
8+
}
9+
public fun id<T: store+drop+copy>(x: Store<T>): T {
10+
x.o
11+
}
12+
fun init_module(account: &signer) {
13+
let v = Store { o: option::none() };
14+
let f: ||option::Option<u64> has copy+drop+store = || id(v);
15+
move_to(account, FunctionStore { f });
16+
}
17+
entry fun entry_func() {
18+
let v = Store { o: option::none() };
19+
let _f: ||option::Option<u64> has copy+drop+store = || id(v);
20+
}
21+
}

0 commit comments

Comments
 (0)