Skip to content

Commit d3f96de

Browse files
i582sansx
authored andcommitted
fix: clarify error message for a field access in bounced types that does not fit in 224 bytes (tact-lang#1111)
1 parent 69938f9 commit d3f96de

6 files changed

+111
-13
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3434
- Docs: complete overhaul of the exit codes page: PR [#978](https://github.com/tact-lang/tact/pull/978)
3535
- Docs: enhanced Jettons Cookbook page: PR [#944](https://github.com/tact-lang/tact/pull/944)
3636
- Error codes in the report are now formatted as a list: PR [#1051](https://github.com/tact-lang/tact/pull/1051)
37+
- Clarify error message for bounced types from which accessed a field that does not fit in 224 bytes: PR [#1111](https://github.com/tact-lang/tact/pull/1111)
3738
- Docs: note that `compilables/` can sometimes be used over `wrappers/` in Blueprint projects: PR [#1112](https://github.com/tact-lang/tact/pull/1112)
3839

3940
### Fixed

src/types/__snapshots__/resolveStatements.spec.ts.snap

+31-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ Line 10, col 5:
8181
`;
8282

8383
exports[`resolveStatements should fail statements for bounced-type-is-smaller 1`] = `
84-
"<unknown>:23:22: Type bounced<"A"> does not have a field named "c"
84+
"<unknown>:23:22: Maximum size of the bounced message is 224 bytes, but the "c" field of type "A" cannot fit into it due to the size of previous fields or its own size, so it cannot be accessed. Make the type of the fields before this one smaller, or reduce the type of this field so that it fits into 224 bytes
8585
Line 23, col 22:
8686
22 | let y: Bool = src.b;
8787
> 23 | let z: Int = src.c;
@@ -1238,6 +1238,36 @@ Line 9, col 5:
12381238
"
12391239
`;
12401240

1241+
exports[`resolveStatements should fail statements for usage-of-bounced-field-in-type-that-is-too-big 1`] = `
1242+
"<unknown>:14:29: Maximum size of the bounced message is 224 bytes, but the "amount" field of type "Withdraw" cannot fit into it due to the size of previous fields or its own size, so it cannot be accessed. Make the type of the fields before this one smaller, or reduce the type of this field so that it fits into 224 bytes
1243+
Line 14, col 29:
1244+
13 | self.balance += msg.data; // ok
1245+
> 14 | self.balance += msg.amount;
1246+
^~~~~~
1247+
15 | }
1248+
"
1249+
`;
1250+
1251+
exports[`resolveStatements should fail statements for usage-of-bounced-field-in-type-that-is-too-big-2 1`] = `
1252+
"<unknown>:26:29: Maximum size of the bounced message is 224 bytes, but the "amount" field of type "Withdraw" cannot fit into it due to the size of previous fields or its own size, so it cannot be accessed. Make the type of the fields before this one smaller, or reduce the type of this field so that it fits into 224 bytes
1253+
Line 26, col 29:
1254+
25 |
1255+
> 26 | self.balance += msg.amount;
1256+
^~~~~~
1257+
27 | }
1258+
"
1259+
`;
1260+
1261+
exports[`resolveStatements should fail statements for usage-of-bounced-field-that-is-too-big 1`] = `
1262+
"<unknown>:12:29: Maximum size of the bounced message is 224 bytes, but the "amount" field of type "Withdraw" cannot fit into it because its too big, so it cannot be accessed. Reduce the type of this field so that it fits into 224 bytes
1263+
Line 12, col 29:
1264+
11 | bounced(msg: bounced<Withdraw>){
1265+
> 12 | self.balance += msg.amount;
1266+
^~~~~~
1267+
13 | }
1268+
"
1269+
`;
1270+
12411271
exports[`resolveStatements should fail statements for var-does-not-exist 1`] = `
12421272
"<unknown>:5:9: Unable to resolve id 'nonExistentVariable'
12431273
Line 5, col 9:

src/types/resolveExpression.ts

+21-12
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,7 @@ import {
2626
hasStaticConstant,
2727
hasStaticFunction,
2828
} from "./resolveDescriptors";
29-
import {
30-
FieldDescription,
31-
printTypeRef,
32-
TypeRef,
33-
typeRefEquals,
34-
} from "./types";
29+
import { printTypeRef, TypeRef, typeRefEquals } from "./types";
3530
import { StatementContext } from "./resolveStatements";
3631
import { MapFunctions } from "../abi/map";
3732
import { GlobalFunctions } from "../abi/global";
@@ -419,16 +414,30 @@ function resolveFieldAccess(
419414
}
420415

421416
// Find field
422-
let fields: FieldDescription[];
423-
424417
const srcT = getType(ctx, src.name);
425418

426-
fields = srcT.fields;
427-
if (src.kind === "ref_bounced") {
428-
fields = fields.slice(0, srcT.partialFieldCount);
419+
const fieldIndex = srcT.fields.findIndex((v) => eqNames(v.name, exp.field));
420+
const field = fieldIndex !== -1 ? srcT.fields[fieldIndex] : undefined;
421+
422+
// If we found a field of bounced<T>, check if the field doesn't fit in 224 bytes and cannot be accessed
423+
if (
424+
src.kind === "ref_bounced" &&
425+
field &&
426+
fieldIndex >= srcT.partialFieldCount
427+
) {
428+
if (srcT.fields.length === 1) {
429+
throwCompilationError(
430+
`Maximum size of the bounced message is 224 bytes, but the ${idTextErr(exp.field)} field of type ${idTextErr(src.name)} cannot fit into it because its too big, so it cannot be accessed. Reduce the type of this field so that it fits into 224 bytes`,
431+
exp.field.loc,
432+
);
433+
}
434+
435+
throwCompilationError(
436+
`Maximum size of the bounced message is 224 bytes, but the ${idTextErr(exp.field)} field of type ${idTextErr(src.name)} cannot fit into it due to the size of previous fields or its own size, so it cannot be accessed. Make the type of the fields before this one smaller, or reduce the type of this field so that it fits into 224 bytes`,
437+
exp.field.loc,
438+
);
429439
}
430440

431-
const field = fields.find((v) => eqNames(v.name, exp.field));
432441
const cst = srcT.constants.find((v) => eqNames(v.name, exp.field));
433442
if (!field && !cst) {
434443
const typeStr =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
primitive Int;
2+
trait BaseTrait { }
3+
4+
message Withdraw {
5+
data128: Int as uint128; // 128
6+
data64: Int as uint64; // 192
7+
data16: Int as uint16; // 208
8+
data8: Int as uint8; // 216
9+
data4: Int as uint4; // 220
10+
data4_2: Int as uint4; // 224
11+
12+
amount: Int as uint128;
13+
}
14+
15+
contract Fund {
16+
balance: Int as uint256 = 0;
17+
18+
bounced(msg: bounced<Withdraw>){
19+
self.balance += msg.data128; // ok
20+
self.balance += msg.data64; // ok
21+
self.balance += msg.data16; // ok
22+
self.balance += msg.data8; // ok
23+
self.balance += msg.data4; // ok
24+
self.balance += msg.data4_2; // ok
25+
26+
self.balance += msg.amount;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
primitive Int;
2+
trait BaseTrait { }
3+
4+
message Withdraw {
5+
data: Int as uint128;
6+
amount: Int as uint128; // exceeds 224 bytes
7+
}
8+
9+
contract Fund {
10+
balance: Int as uint256 = 0;
11+
12+
bounced(msg: bounced<Withdraw>){
13+
self.balance += msg.data; // ok
14+
self.balance += msg.amount;
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
primitive Int;
2+
trait BaseTrait { }
3+
4+
message Withdraw {
5+
amount: Int as uint256; // too big
6+
}
7+
8+
contract Fund {
9+
balance: Int as uint256 = 0;
10+
11+
bounced(msg: bounced<Withdraw>){
12+
self.balance += msg.amount;
13+
}
14+
}

0 commit comments

Comments
 (0)