Skip to content

Commit a1822a4

Browse files
authored
fix bug 13201 by emitting an empty vector constant load as a VecPack instead (aptos-labs#13205)
* Check `LdConst` in bytecode generation to make sure we don't try to load a constant containing an empty vector, as the type will be unknown; instead, use `VecPack`. * Do it recursively to deal with constant vectors containing empty vectors.
1 parent 387de66 commit a1822a4

31 files changed

+634
-9
lines changed

third_party/move/move-compiler-v2/src/file_format_generator/function_generator.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,11 @@ impl<'a> FunctionGenerator<'a> {
686686
/// Generate code for the load instruction.
687687
fn gen_load(&mut self, ctx: &BytecodeContext, dest: &TempIndex, cons: &Constant) {
688688
self.flush_any_conflicts(ctx, std::slice::from_ref(dest), &[]);
689+
self.gen_load_push(ctx, cons, ctx.fun_ctx.fun.get_local_type(*dest));
690+
self.abstract_push_result(ctx, vec![*dest]);
691+
}
692+
693+
fn gen_load_push(&mut self, ctx: &BytecodeContext, cons: &Constant, dest_type: &Type) {
689694
use Constant::*;
690695
match cons {
691696
Bool(b) => {
@@ -703,17 +708,46 @@ impl<'a> FunctionGenerator<'a> {
703708
U256(n) => self.emit(FF::Bytecode::LdU256(
704709
move_core_types::u256::U256::from_le_bytes(&n.to_le_bytes()),
705710
)),
711+
Vector(vec) if Self::vector_is_empty_or_contains_empty_vector(vec) => {
712+
self.gen_vector_load_push(ctx, vec, dest_type);
713+
},
706714
_ => {
707-
let cons = self.gen.constant_index(
708-
&ctx.fun_ctx.module,
709-
&ctx.fun_ctx.loc,
710-
cons,
711-
ctx.fun_ctx.fun.get_local_type(*dest),
712-
);
715+
let cons =
716+
self.gen
717+
.constant_index(&ctx.fun_ctx.module, &ctx.fun_ctx.loc, cons, dest_type);
713718
self.emit(FF::Bytecode::LdConst(cons));
714719
},
715720
}
716-
self.abstract_push_result(ctx, vec![*dest]);
721+
}
722+
723+
fn vector_is_empty_or_contains_empty_vector(cons: &Vec<Constant>) -> bool {
724+
cons.is_empty()
725+
|| cons.iter().any(|cons| match cons {
726+
Constant::Vector(vec) => Self::vector_is_empty_or_contains_empty_vector(vec),
727+
_ => false,
728+
})
729+
}
730+
731+
fn gen_vector_load_push(
732+
&mut self,
733+
ctx: &BytecodeContext,
734+
vec: &Vec<Constant>,
735+
vec_type: &Type,
736+
) {
737+
let fun_ctx = ctx.fun_ctx;
738+
let elem_type = if let Type::Vector(el) = vec_type {
739+
el.as_ref().clone()
740+
} else {
741+
fun_ctx.internal_error("expected vector type");
742+
Type::new_prim(PrimitiveType::Bool)
743+
};
744+
for cons in vec.iter() {
745+
self.gen_load_push(ctx, cons, &elem_type);
746+
}
747+
let sign = self
748+
.gen
749+
.signature(&fun_ctx.module, &fun_ctx.loc, vec![elem_type]);
750+
self.emit(FF::Bytecode::VecPack(sign, vec.len() as u64));
717751
}
718752

719753
/// Generates code for an inline spec block. The spec block needs
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// -- Model dump before bytecode pipeline
2+
module 0x42::m {
3+
use std::bcs;
4+
use std::string::{Self};
5+
use std::vector;
6+
public entry fun init() {
7+
{
8+
let _: vector<string::String> = {
9+
let result: vector<string::String> = Vector<string::String>();
10+
{
11+
let (v: vector<vector<u8>>): (vector<vector<u8>>) = Tuple([]);
12+
vector::reverse<vector<u8>>(Borrow(Mutable)(v));
13+
loop {
14+
if Not(vector::is_empty<vector<u8>>(Borrow(Immutable)(v))) {
15+
{
16+
let e: vector<u8> = vector::pop_back<vector<u8>>(Borrow(Mutable)(v));
17+
{
18+
let (elem: vector<u8>): (vector<u8>) = Tuple(e);
19+
vector::push_back<string::String>(Borrow(Mutable)(result), {
20+
let (key: vector<u8>): (vector<u8>) = Tuple(elem);
21+
string::utf8(key)
22+
})
23+
};
24+
Tuple()
25+
}
26+
} else {
27+
break
28+
}
29+
};
30+
Tuple()
31+
};
32+
result
33+
};
34+
{
35+
let _: vector<vector<u8>> = {
36+
let result: vector<vector<u8>> = Vector<vector<u8>>();
37+
{
38+
let (v: vector<u64>): (vector<u64>) = Tuple([]);
39+
vector::reverse<u64>(Borrow(Mutable)(v));
40+
loop {
41+
if Not(vector::is_empty<u64>(Borrow(Immutable)(v))) {
42+
{
43+
let e: u64 = vector::pop_back<u64>(Borrow(Mutable)(v));
44+
{
45+
let (elem: u64): (u64) = Tuple(e);
46+
vector::push_back<vector<u8>>(Borrow(Mutable)(result), {
47+
let (v: u64): (u64) = Tuple(elem);
48+
bcs::to_bytes<u64>(Borrow(Immutable)(v))
49+
})
50+
};
51+
Tuple()
52+
}
53+
} else {
54+
break
55+
}
56+
};
57+
Tuple()
58+
};
59+
result
60+
};
61+
Tuple()
62+
}
63+
}
64+
}
65+
} // end 0x42::m
66+
67+
68+
============ bytecode verification succeeded ========
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//# publish
2+
module 0x42::m {
3+
use std::bcs;
4+
use std::string::{Self};
5+
use std::vector;
6+
7+
const KEYS: vector<vector<u8>> = vector[];
8+
const VALUES: vector<u64> = vector[];
9+
10+
public entry fun init(
11+
) {
12+
let _ = vector::map(KEYS, |key|{ string::utf8(key)});
13+
let _ = vector::map(VALUES, |v|{ bcs::to_bytes<u64>(&v)});
14+
}
15+
}
16+
17+
//# run 0x42::m::init
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// -- Model dump before bytecode pipeline
2+
module 0x42::m {
3+
use std::vector;
4+
public entry fun init() {
5+
{
6+
let _x: vector<u64> = {
7+
let result: vector<u64> = Vector<u64>();
8+
{
9+
let (v: vector<vector<u8>>): (vector<vector<u8>>) = Tuple([]);
10+
vector::reverse<vector<u8>>(Borrow(Mutable)(v));
11+
loop {
12+
if Not(vector::is_empty<vector<u8>>(Borrow(Immutable)(v))) {
13+
{
14+
let e: vector<u8> = vector::pop_back<vector<u8>>(Borrow(Mutable)(v));
15+
{
16+
let (elem: vector<u8>): (vector<u8>) = Tuple(e);
17+
vector::push_back<u64>(Borrow(Mutable)(result), {
18+
let (key: vector<u8>): (vector<u8>) = Tuple(elem);
19+
{
20+
let t: vector<u8> = key;
21+
Add<u64>(vector::length<u8>(Borrow(Immutable)(t)), 2)
22+
}
23+
})
24+
};
25+
Tuple()
26+
}
27+
} else {
28+
break
29+
}
30+
};
31+
Tuple()
32+
};
33+
result
34+
};
35+
{
36+
let _y: vector<u64> = {
37+
let result: vector<u64> = Vector<u64>();
38+
{
39+
let (v: vector<u64>): (vector<u64>) = Tuple([]);
40+
vector::reverse<u64>(Borrow(Mutable)(v));
41+
loop {
42+
if Not(vector::is_empty<u64>(Borrow(Immutable)(v))) {
43+
{
44+
let e: u64 = vector::pop_back<u64>(Borrow(Mutable)(v));
45+
{
46+
let (elem: u64): (u64) = Tuple(e);
47+
vector::push_back<u64>(Borrow(Mutable)(result), {
48+
let (v: u64): (u64) = Tuple(elem);
49+
Add<u64>(v, 3)
50+
})
51+
};
52+
Tuple()
53+
}
54+
} else {
55+
break
56+
}
57+
};
58+
Tuple()
59+
};
60+
result
61+
};
62+
Tuple()
63+
}
64+
}
65+
}
66+
} // end 0x42::m
67+
68+
69+
============ bytecode verification succeeded ========
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//# publish
2+
module 0x42::m {
3+
use std::vector;
4+
const KEYS: vector<vector<u8>> = vector[];
5+
const VALUES: vector<u64> = vector[];
6+
7+
public entry fun init() {
8+
let _x: vector<u64> = vector::map<vector<u8>, u64>(KEYS, |key| { let t: vector<u8> = key; (vector::length<u8>(&t) + 2) });
9+
let _y: vector<u64> = vector::map<u64, u64>(VALUES, |v| { (v + 3u64) });
10+
}
11+
}
12+
13+
//# run 0x42::m::init
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// -- Model dump before bytecode pipeline
2+
module 0x42::m {
3+
use std::vector;
4+
public entry fun init() {
5+
{
6+
let _x: vector<u64> = {
7+
let (v: vector<vector<u8>>): (vector<vector<u8>>) = Tuple(Deref(vector::borrow<vector<vector<u8>>>(Borrow(Immutable)([Vector([])]), 0)));
8+
{
9+
let result: vector<u64> = Vector<u64>();
10+
{
11+
let (v: vector<vector<u8>>): (vector<vector<u8>>) = Tuple(v);
12+
vector::reverse<vector<u8>>(Borrow(Mutable)(v));
13+
loop {
14+
if Not(vector::is_empty<vector<u8>>(Borrow(Immutable)(v))) {
15+
{
16+
let e: vector<u8> = vector::pop_back<vector<u8>>(Borrow(Mutable)(v));
17+
{
18+
let (elem: vector<u8>): (vector<u8>) = Tuple(e);
19+
vector::push_back<u64>(Borrow(Mutable)(result), {
20+
let (key: vector<u8>): (vector<u8>) = Tuple(elem);
21+
{
22+
let t: vector<u8> = key;
23+
Add<u64>(vector::length<u8>(Borrow(Immutable)(t)), 2)
24+
}
25+
})
26+
};
27+
Tuple()
28+
}
29+
} else {
30+
break
31+
}
32+
};
33+
Tuple()
34+
};
35+
result
36+
}
37+
};
38+
{
39+
let _y: vector<u64> = {
40+
let (v: vector<u64>): (vector<u64>) = Tuple(Deref(vector::borrow<vector<u64>>(Borrow(Immutable)([Vector([])]), 0)));
41+
{
42+
let result: vector<u64> = Vector<u64>();
43+
{
44+
let (v: vector<u64>): (vector<u64>) = Tuple(v);
45+
vector::reverse<u64>(Borrow(Mutable)(v));
46+
loop {
47+
if Not(vector::is_empty<u64>(Borrow(Immutable)(v))) {
48+
{
49+
let e: u64 = vector::pop_back<u64>(Borrow(Mutable)(v));
50+
{
51+
let (elem: u64): (u64) = Tuple(e);
52+
vector::push_back<u64>(Borrow(Mutable)(result), {
53+
let (v: u64): (u64) = Tuple(elem);
54+
Add<u64>(v, 3)
55+
})
56+
};
57+
Tuple()
58+
}
59+
} else {
60+
break
61+
}
62+
};
63+
Tuple()
64+
};
65+
result
66+
}
67+
};
68+
Tuple()
69+
}
70+
}
71+
}
72+
} // end 0x42::m
73+
74+
75+
============ bytecode verification succeeded ========
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//# publish
2+
module 0x42::m {
3+
use std::vector;
4+
const KEYS: vector<vector<vector<u8>>> = vector[vector[]];
5+
const VALUES: vector<vector<u64>> = vector[vector[]];
6+
7+
public entry fun init() {
8+
let _x: vector<u64> = vector::map<vector<u8>, u64>(*vector::borrow(&KEYS, 0), |key| { let t: vector<u8> = key; (vector::length<u8>(&t) + 2) });
9+
let _y: vector<u64> = vector::map<u64, u64>(*vector::borrow(&VALUES, 0), |v| { (v + 3u64) });
10+
}
11+
}
12+
13+
//# run 0x42::m::init
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// -- Model dump before bytecode pipeline
2+
module 0x42::m {
3+
use std::bcs;
4+
use std::string::{Self};
5+
use std::vector;
6+
public entry fun init() {
7+
{
8+
let _: vector<string::String> = {
9+
let result: vector<string::String> = Vector<string::String>();
10+
{
11+
let (v: vector<vector<u8>>): (vector<vector<u8>>) = Tuple([Vector([Number(3)])]);
12+
vector::reverse<vector<u8>>(Borrow(Mutable)(v));
13+
loop {
14+
if Not(vector::is_empty<vector<u8>>(Borrow(Immutable)(v))) {
15+
{
16+
let e: vector<u8> = vector::pop_back<vector<u8>>(Borrow(Mutable)(v));
17+
{
18+
let (elem: vector<u8>): (vector<u8>) = Tuple(e);
19+
vector::push_back<string::String>(Borrow(Mutable)(result), {
20+
let (key: vector<u8>): (vector<u8>) = Tuple(elem);
21+
string::utf8(key)
22+
})
23+
};
24+
Tuple()
25+
}
26+
} else {
27+
break
28+
}
29+
};
30+
Tuple()
31+
};
32+
result
33+
};
34+
{
35+
let _: vector<vector<u8>> = {
36+
let result: vector<vector<u8>> = Vector<vector<u8>>();
37+
{
38+
let (v: vector<u64>): (vector<u64>) = Tuple([Number(3)]);
39+
vector::reverse<u64>(Borrow(Mutable)(v));
40+
loop {
41+
if Not(vector::is_empty<u64>(Borrow(Immutable)(v))) {
42+
{
43+
let e: u64 = vector::pop_back<u64>(Borrow(Mutable)(v));
44+
{
45+
let (elem: u64): (u64) = Tuple(e);
46+
vector::push_back<vector<u8>>(Borrow(Mutable)(result), {
47+
let (v: u64): (u64) = Tuple(elem);
48+
bcs::to_bytes<u64>(Borrow(Immutable)(v))
49+
})
50+
};
51+
Tuple()
52+
}
53+
} else {
54+
break
55+
}
56+
};
57+
Tuple()
58+
};
59+
result
60+
};
61+
Tuple()
62+
}
63+
}
64+
}
65+
} // end 0x42::m
66+
67+
68+
============ bytecode verification succeeded ========

0 commit comments

Comments
 (0)