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

[compiler-v2] Add loop labels to the language #14868

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 2 additions & 5 deletions third_party/move/move-compiler-v2/src/bytecode_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,11 +447,11 @@ impl<'env> Generator<'env> {
self.emit_with(*id, |attr| Bytecode::Jump(attr, continue_label));
self.emit_with(*id, |attr| Bytecode::Label(attr, break_label));
},
ExpData::LoopCont(id, 0, do_continue) => {
ExpData::LoopCont(id, nest, do_continue) => {
if let Some(LoopContext {
continue_label,
break_label,
}) = self.loops.last()
}) = self.loops.iter().rev().nth(*nest)
{
let target = if *do_continue {
*continue_label
Expand All @@ -463,9 +463,6 @@ impl<'env> Generator<'env> {
self.error(*id, "missing enclosing loop statement")
}
},
ExpData::LoopCont(_, _, _) => {
unimplemented!("continue/break with nesting")
},
ExpData::SpecBlock(id, spec) => {
// Map locals in spec to assigned temporaries.
let mut replacer = |id, target| {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// -- Model dump before bytecode pipeline
module 0x815::test {
private fun f1() {
loop {
loop {
loop {
if true {
loop {
if false {
continue[3]
} else {
break[1]
};
break
}
} else {
continue[2]
}
}
};
break
}
}
} // end 0x815::test

// -- Sourcified model before bytecode pipeline
module 0x815::test {
fun f1() {
'l0: loop {
loop 'l1: loop if (true) loop {
if (false) continue 'l0 else break 'l1;
break
} else continue 'l0;
break
}
}
}

============ initial bytecode ================

[variant baseline]
fun test::f1() {
var $t0: bool
var $t1: bool
0: label L0
1: label L2
2: label L4
3: $t0 := true
4: if ($t0) goto 5 else goto 19
5: label L6
6: label L9
7: $t1 := false
8: if ($t1) goto 9 else goto 12
9: label L11
10: goto 0
11: goto 14
12: label L12
13: goto 23
14: label L13
15: goto 17
16: goto 6
17: label L10
18: goto 21
19: label L7
20: goto 0
21: label L8
22: goto 2
23: label L5
24: goto 1
25: label L3
26: goto 28
27: goto 0
28: label L1
29: return ()
}


============ bytecode verification succeeded ========
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Diagnostics:
error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:3:9
3 │ 'outer: loop {
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:6:17
6 │ 'inner: loop if (true) loop {
│ ^^^^^^ Move language construct `'inner` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:7:41
7 │ if (false) continue 'outer else break 'inner;
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:7:59
7 │ if (false) continue 'outer else break 'inner;
│ ^^^^^^ Move language construct `'inner` is not enabled until version 2.1

error: unsupported language construct
┌─ tests/checking-lang-v1/loop_labels.move:9:33
9 │ } else continue 'outer
│ ^^^^^^ Move language construct `'outer` is not enabled until version 2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

Diagnostics:
error: label `'outer` undefined
┌─ tests/checking/control_flow/loop_labels_check_err.move:3:15
3 │ break 'outer;
│ ^^^^^^

error: label `'inner` undefined
┌─ tests/checking/control_flow/loop_labels_check_err.move:5:19
5 │ break 'inner
│ ^^^^^^

error: label `'l1` already used by outer loop
┌─ tests/checking/control_flow/loop_labels_check_err.move:11:19
11 │ 'l1: loop 'l1: loop {};
│ --- ^^^
│ │
│ outer definition of label
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun undefined_label() {
break 'outer;
'outer: loop {
break 'inner
}
}

fun duplicate_label() {
'l1: loop {};
'l1: loop 'l1: loop {};
'l1: loop {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// -- Model dump before bytecode pipeline
module 0x815::test {
private fun f1() {
loop {
loop {
loop {
if true {
loop {
if false {
continue[3]
} else {
break[1]
};
break
}
} else {
continue[2]
}
}
};
break
}
}
} // end 0x815::test

// -- Sourcified model before bytecode pipeline
module 0x815::test {
fun f1() {
'l0: loop {
loop 'l1: loop if (true) loop {
if (false) continue 'l0 else break 'l1;
break
} else continue 'l0;
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module 0x815::test {
fun f1() {
'outer: loop {
// unlabeled loop, but counts in nesting in AST
loop {
'inner: loop if (true) loop {
if (false) continue 'outer else break 'inner;
break
} else continue 'outer
};
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Diagnostics:
error: unexpected token
┌─ tests/checking/control_flow/loop_labels_parse_err1.move:3:13
3 │ 'a: if (true) false else true
│ ^^
│ │
│ Unexpected 'if'
│ Expected one of: `while` or `loop`
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module 0x815::test {
fun f1(): bool {
'a: if (true) false else true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Diagnostics:
error: unexpected token
┌─ tests/checking/control_flow/loop_labels_parse_err2.move:3:13
3 │ 'a: if (true) false else true
│ ^^
│ │
│ Unexpected 'if'
│ Expected one of: `while` or `loop`
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module 0x815::test {
fun f1(): bool {
'a: if (true) false else true
}
}
4 changes: 2 additions & 2 deletions third_party/move/move-compiler-v2/tests/testsuite.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const TEST_CONFIGS: Lazy<BTreeMap<&str, TestConfig>> = Lazy::new(|| {
// Turn optimization on by default. Some configs below may turn it off.
.set_experiment(Experiment::OPTIMIZE, true)
.set_experiment(Experiment::OPTIMIZE_WAITING_FOR_COMPARE_TESTS, true)
.set_language_version(LanguageVersion::V2_0);
.set_language_version(LanguageVersion::V2_1);
opts.testing = true;
let configs = vec![
// --- Tests for checking and ast processing
Expand Down Expand Up @@ -723,7 +723,7 @@ const TEST_CONFIGS: Lazy<BTreeMap<&str, TestConfig>> = Lazy::new(|| {
include: vec!["/op-equal/"],
exclude: vec![],
exp_suffix: None,
options: opts.clone().set_language_version(LanguageVersion::V2_1),
options: opts.clone(),
// Run the entire compiler pipeline to double-check the result
stop_after: StopAfter::FileFormat,
dump_ast: DumpLevel::EndStage,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
processed 1 task
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//# run
script {
fun main() {
let result = 0;
'outer: while (result < 100) {
while (result < 50) {
'inner: while (result < 30) {
result += 1;
continue 'outer
};
result += 10;
continue 'outer
};
result += 20
};
assert!(result == 110);
}
}
Loading
Loading