Skip to content

Commit

Permalink
fix: clarify error message for a field access in bounced types that d…
Browse files Browse the repository at this point in the history
…oes not fit in 224 bytes (#1111)
  • Loading branch information
i582 authored Dec 5, 2024
1 parent d43373b commit ecd8738
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Docs: complete overhaul of the exit codes page: PR [#978](https://github.com/tact-lang/tact/pull/978)
- Docs: enhanced Jettons Cookbook page: PR [#944](https://github.com/tact-lang/tact/pull/944)
- Error codes in the report are now formatted as a list: PR [#1051](https://github.com/tact-lang/tact/pull/1051)
- 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)
- Docs: note that `compilables/` can sometimes be used over `wrappers/` in Blueprint projects: PR [#1112](https://github.com/tact-lang/tact/pull/1112)

### Fixed
Expand Down
32 changes: 31 additions & 1 deletion src/types/__snapshots__/resolveStatements.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Line 10, col 5:
`;

exports[`resolveStatements should fail statements for bounced-type-is-smaller 1`] = `
"<unknown>:23:22: Type bounced<"A"> does not have a field named "c"
"<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
Line 23, col 22:
22 | let y: Bool = src.b;
> 23 | let z: Int = src.c;
Expand Down Expand Up @@ -1238,6 +1238,36 @@ Line 9, col 5:
"
`;

exports[`resolveStatements should fail statements for usage-of-bounced-field-in-type-that-is-too-big 1`] = `
"<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
Line 14, col 29:
13 | self.balance += msg.data; // ok
> 14 | self.balance += msg.amount;
^~~~~~
15 | }
"
`;

exports[`resolveStatements should fail statements for usage-of-bounced-field-in-type-that-is-too-big-2 1`] = `
"<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
Line 26, col 29:
25 |
> 26 | self.balance += msg.amount;
^~~~~~
27 | }
"
`;

exports[`resolveStatements should fail statements for usage-of-bounced-field-that-is-too-big 1`] = `
"<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
Line 12, col 29:
11 | bounced(msg: bounced<Withdraw>){
> 12 | self.balance += msg.amount;
^~~~~~
13 | }
"
`;

exports[`resolveStatements should fail statements for var-does-not-exist 1`] = `
"<unknown>:5:9: Unable to resolve id 'nonExistentVariable'
Line 5, col 9:
Expand Down
33 changes: 21 additions & 12 deletions src/types/resolveExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,7 @@ import {
hasStaticConstant,
hasStaticFunction,
} from "./resolveDescriptors";
import {
FieldDescription,
printTypeRef,
TypeRef,
typeRefEquals,
} from "./types";
import { printTypeRef, TypeRef, typeRefEquals } from "./types";
import { StatementContext } from "./resolveStatements";
import { MapFunctions } from "../abi/map";
import { GlobalFunctions } from "../abi/global";
Expand Down Expand Up @@ -419,16 +414,30 @@ function resolveFieldAccess(
}

// Find field
let fields: FieldDescription[];

const srcT = getType(ctx, src.name);

fields = srcT.fields;
if (src.kind === "ref_bounced") {
fields = fields.slice(0, srcT.partialFieldCount);
const fieldIndex = srcT.fields.findIndex((v) => eqNames(v.name, exp.field));
const field = fieldIndex !== -1 ? srcT.fields[fieldIndex] : undefined;

// If we found a field of bounced<T>, check if the field doesn't fit in 224 bytes and cannot be accessed
if (
src.kind === "ref_bounced" &&
field &&
fieldIndex >= srcT.partialFieldCount
) {
if (srcT.fields.length === 1) {
throwCompilationError(
`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`,
exp.field.loc,
);
}

throwCompilationError(
`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`,
exp.field.loc,
);
}

const field = fields.find((v) => eqNames(v.name, exp.field));
const cst = srcT.constants.find((v) => eqNames(v.name, exp.field));
if (!field && !cst) {
const typeStr =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
primitive Int;
trait BaseTrait { }

message Withdraw {
data128: Int as uint128; // 128
data64: Int as uint64; // 192
data16: Int as uint16; // 208
data8: Int as uint8; // 216
data4: Int as uint4; // 220
data4_2: Int as uint4; // 224

amount: Int as uint128;
}

contract Fund {
balance: Int as uint256 = 0;

bounced(msg: bounced<Withdraw>){
self.balance += msg.data128; // ok
self.balance += msg.data64; // ok
self.balance += msg.data16; // ok
self.balance += msg.data8; // ok
self.balance += msg.data4; // ok
self.balance += msg.data4_2; // ok

self.balance += msg.amount;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
primitive Int;
trait BaseTrait { }

message Withdraw {
data: Int as uint128;
amount: Int as uint128; // exceeds 224 bytes
}

contract Fund {
balance: Int as uint256 = 0;

bounced(msg: bounced<Withdraw>){
self.balance += msg.data; // ok
self.balance += msg.amount;
}
}
14 changes: 14 additions & 0 deletions src/types/stmts-failed/usage-of-bounced-field-that-is-too-big.tact
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
primitive Int;
trait BaseTrait { }

message Withdraw {
amount: Int as uint256; // too big
}

contract Fund {
balance: Int as uint256 = 0;

bounced(msg: bounced<Withdraw>){
self.balance += msg.amount;
}
}

0 comments on commit ecd8738

Please sign in to comment.