Skip to content

Commit 2412182

Browse files
committed
Add (failing) tests for for/while loop compilation and console.log/require handling inside loops
1 parent e50b8ac commit 2412182

13 files changed

Lines changed: 298 additions & 11 deletions

packages/cashc/src/semantic/TypeCheckTraversal.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ export default class TypeCheckTraversal extends AstTraversal {
9696
node.elseBlock = this.visitOptional(node.elseBlock);
9797

9898
if (!implicitlyCastable(node.condition.type, PrimitiveType.BOOL)) {
99-
throw new TypeError(node, node.condition.type, PrimitiveType.BOOL);
99+
throw new TypeError(node.condition, node.condition.type, PrimitiveType.BOOL);
100100
}
101101

102102
return node;
@@ -107,7 +107,7 @@ export default class TypeCheckTraversal extends AstTraversal {
107107
node.block = this.visit(node.block);
108108

109109
if (!implicitlyCastable(node.condition.type, PrimitiveType.BOOL)) {
110-
throw new TypeError(node, node.condition.type, PrimitiveType.BOOL);
110+
throw new TypeError(node.condition, node.condition.type, PrimitiveType.BOOL);
111111
}
112112

113113
return node;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract ForLoopMissingCondition() {
2+
function spend() {
3+
for (int i = 0; ; i = i + 1) {
4+
require(i < 3);
5+
}
6+
}
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
contract ForLoopMissingInit() {
2+
function spend() {
3+
int i = 0;
4+
5+
for (; i < 3; i = i + 1) {
6+
i = i + 1;
7+
}
8+
9+
require(i == 3);
10+
}
11+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract ForLoopMissingUpdate() {
2+
function spend() {
3+
for (int i = 0; i < 3; ) {
4+
require(i < 3);
5+
}
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract ForLoopNonAssignmentUpdate() {
2+
function spend() {
3+
for (int i = 0; i < 3; i + 1) {
4+
require(i < 3);
5+
}
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract ForLoopUpdateDefinition() {
2+
function spend() {
3+
for (int i = 0; i < 3; int j = 0) {
4+
require(i < 3);
5+
}
6+
}
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
contract ForLoopConditionTypeError() {
2+
function spend() {
3+
for (int i = 0; tx.inputs[0].outpointTransactionHash; i = i + 1) {
4+
require(i < 3);
5+
}
6+
}
7+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
contract WhileLoopConditionTypeError() {
2+
function spend() {
3+
int i = 0;
4+
5+
while (tx.inputs[0].outpointTransactionHash) {
6+
i = i + 1;
7+
}
8+
9+
require(i > 0);
10+
}
11+
}

packages/cashc/test/generation/fixtures.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -984,6 +984,125 @@ export const fixtures: Fixture[] = [
984984
updatedAt: '',
985985
},
986986
},
987+
{
988+
fn: 'while_loop_basic.cash',
989+
artifact: {
990+
contractName: 'WhileLoopBasic',
991+
constructorInputs: [],
992+
abi: [{ name: 'spend', inputs: [] }],
993+
bytecode: 'OP_0 OP_DUP OP_3 OP_LESSTHAN OP_IF OP_BEGIN OP_DUP OP_1ADD OP_NIP OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL OP_ENDIF OP_3 OP_NUMEQUAL',
994+
debug: {
995+
bytecode: '0076539f6365768b777653a26668539c',
996+
sourceMap: '3:16:3:17;5:15:5:16;:19::20;:15:::1;:22:7:9:0;:8;6:16:6:17;:::21:1;:12::22;5:15:5:16:0;:19::20;:8:7:9:1;;:22;9:21:9:22:0;:8::24:1',
997+
logs: [],
998+
requires: [
999+
{ ip: 16, line: 9 },
1000+
],
1001+
},
1002+
source: fs.readFileSync(new URL('../valid-contract-files/while_loop_basic.cash', import.meta.url), { encoding: 'utf-8' }),
1003+
compiler: {
1004+
name: 'cashc',
1005+
version,
1006+
options: {
1007+
enforceFunctionParameterTypes: true,
1008+
},
1009+
},
1010+
updatedAt: '',
1011+
},
1012+
},
1013+
{
1014+
fn: 'for_loop_basic.cash',
1015+
artifact: {
1016+
contractName: 'ForLoopBasic',
1017+
constructorInputs: [],
1018+
abi: [{ name: 'spend', inputs: [] }],
1019+
bytecode: 'OP_0 OP_0 OP_DUP OP_3 OP_LESSTHAN OP_IF OP_BEGIN OP_2DUP OP_ADD OP_ROT OP_DROP OP_SWAP OP_DUP OP_1ADD OP_NIP OP_DUP OP_3 OP_GREATERTHANOREQUAL OP_UNTIL OP_ENDIF OP_SWAP OP_3 OP_NUMEQUAL OP_NIP',
1020+
debug: {
1021+
bytecode: '000076539f63656e937b757c768b777653a266687c539c77',
1022+
sourceMap: '3:18:3:19;5:21:5:22;:24::25;:28::29;:24:::1;:42:7:9:0;:8;6:18:6:25;::::1;:12::26;;;5:35:5:36:0;:::40:1;:31;:24::25:0;:28::29;:8:7:9:1;;:42;9:16:9:19:0;:23::24;:8::26:1;2:4:10:5',
1023+
logs: [],
1024+
requires: [
1025+
{ ip: 23, line: 9 },
1026+
],
1027+
},
1028+
source: fs.readFileSync(new URL('../valid-contract-files/for_loop_basic.cash', import.meta.url), { encoding: 'utf-8' }),
1029+
compiler: {
1030+
name: 'cashc',
1031+
version,
1032+
options: {
1033+
enforceFunctionParameterTypes: true,
1034+
},
1035+
},
1036+
updatedAt: '',
1037+
},
1038+
},
1039+
{
1040+
fn: 'for_while_nested.cash',
1041+
artifact: {
1042+
contractName: 'ForWhileNested',
1043+
constructorInputs: [],
1044+
abi: [{ name: 'spend', inputs: [] }],
1045+
bytecode: 'OP_0 OP_0 OP_DUP OP_2 OP_LESSTHAN OP_IF OP_BEGIN OP_0 OP_DUP OP_2 OP_LESSTHAN OP_IF OP_BEGIN OP_2 OP_PICK OP_2 OP_PICK OP_ADD OP_OVER OP_ADD OP_3 OP_ROLL OP_DROP OP_SWAP OP_TOALTSTACK OP_SWAP OP_FROMALTSTACK OP_DUP OP_1ADD OP_NIP OP_DUP OP_2 OP_GREATERTHANOREQUAL OP_UNTIL OP_ENDIF OP_OVER OP_1ADD OP_ROT OP_DROP OP_NIP OP_DUP OP_2 OP_GREATERTHANOREQUAL OP_UNTIL OP_ENDIF OP_SWAP OP_4 OP_NUMEQUAL OP_NIP',
1046+
debug: {
1047+
bytecode: '000076529f63650076529f636552795279937893537a757c6b7c6c768b777652a26668788b7b75777652a266687c549c77',
1048+
sourceMap: '3:18:3:19;5:21:5:22;:24::25;:28::29;:24:::1;:42:13:9:0;:8;6:20:6:21;8:19:8:20;:23::24;:19:::1;:26:12:13:0;:12;9:22:9:25;;:28::29;;:22:::1;:32::33:0;:22:::1;:16::34;;;;;;;10:20:10:21:0;:::25:1;:16::26;8:19:8:20:0;:23::24;:12:12:13:1;;:26;5:35:5:36:0;:::40:1;:31;;::13:9;:24:5:25:0;:28::29;:8:13:9:1;;:42;15:16:15:19:0;:23::24;:8::26:1;2:4:16:5',
1049+
logs: [
1050+
{
1051+
ip: 30,
1052+
line: 11,
1053+
data: [
1054+
'sum:',
1055+
{ stackIndex: 2, type: 'int', ip: 30 },
1056+
'i:',
1057+
{ stackIndex: 1, type: 'int', ip: 30 },
1058+
'j:',
1059+
{ stackIndex: 0, type: 'int', ip: 30 },
1060+
],
1061+
},
1062+
],
1063+
requires: [
1064+
{ ip: 48, line: 15 },
1065+
],
1066+
},
1067+
source: fs.readFileSync(new URL('../valid-contract-files/for_while_nested.cash', import.meta.url), { encoding: 'utf-8' }),
1068+
compiler: {
1069+
name: 'cashc',
1070+
version,
1071+
options: {
1072+
enforceFunctionParameterTypes: true,
1073+
},
1074+
},
1075+
updatedAt: '',
1076+
},
1077+
},
1078+
{
1079+
fn: 'while_loop.cash',
1080+
artifact: {
1081+
contractName: 'Loopy',
1082+
constructorInputs: [],
1083+
abi: [{ name: 'doLoop', inputs: [] }],
1084+
bytecode: 'OP_0 OP_DUP OP_TXINPUTCOUNT OP_LESSTHAN OP_IF OP_BEGIN OP_DUP OP_1ADD OP_NIP OP_DUP OP_TXINPUTCOUNT OP_GREATERTHANOREQUAL OP_UNTIL OP_ENDIF OP_2 OP_GREATERTHAN',
1085+
debug: {
1086+
bytecode: '0076c39f6365768b7776c3a2666852a0',
1087+
sourceMap: '3:16:3:17;5:15:5:16;:19::35;:15:::1;:37:7:9:0;:8;6:16:6:17;:::21:1;:12::22;5:15:5:16:0;:19::35;:8:7:9:1;;:37;10:20:10:21:0;:8::23:1',
1088+
logs: [
1089+
{ ip: 14, line: 9, data: [{ stackIndex: 0, type: 'int', ip: 14 }] },
1090+
],
1091+
requires: [
1092+
{ ip: 16, line: 10 },
1093+
],
1094+
},
1095+
source: fs.readFileSync(new URL('../valid-contract-files/while_loop.cash', import.meta.url), { encoding: 'utf-8' }),
1096+
compiler: {
1097+
name: 'cashc',
1098+
version,
1099+
options: {
1100+
enforceFunctionParameterTypes: true,
1101+
},
1102+
},
1103+
updatedAt: '',
1104+
},
1105+
},
9871106
{
9881107
fn: 'complex_loop.cash',
9891108
artifact: {

packages/cashc/test/semantic/LoopLoweringTraversal.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ describe('LoopLoweringTraversal', () => {
114114
const doWhileStatements = loweredDoWhile.block.statements ?? [];
115115
const finalDoWhileStatement = doWhileStatements[doWhileStatements.length - 1];
116116

117+
const originalSumAdditionStatement = originalFor.block.statements?.[0] as AssignNode;
118+
const loweredSumAdditionStatement = loweredDoWhile.block.statements?.[0] as AssignNode;
119+
117120
expect(scopedBlock).toBeInstanceOf(BlockNode);
118121
expect(init).toBeInstanceOf(VariableDefinitionNode);
119122
expect(loopBranch).toBeInstanceOf(BranchNode);
@@ -136,6 +139,7 @@ describe('LoopLoweringTraversal', () => {
136139
expect(loweredDoWhile.location).toBe(originalFor.location);
137140
expect(loopBranch.condition.location).toBe(originalCondition.location);
138141
expect(loweredDoWhile.condition.location).toBe(originalCondition.location);
142+
expect(loweredSumAdditionStatement.location).toBe(originalSumAdditionStatement.location);
139143
});
140144

141145
it('should recursively lower nested while/for loops', () => {

0 commit comments

Comments
 (0)