mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge pull request #1543 from ton-blockchain/testnet
Merge developer branch
This commit is contained in:
commit
0439613bff
184 changed files with 6872 additions and 2243 deletions
17
.github/workflows/docker-ubuntu-branch-image.yml
vendored
17
.github/workflows/docker-ubuntu-branch-image.yml
vendored
|
@ -20,10 +20,12 @@ jobs:
|
|||
submodules: 'recursive'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v3.5.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
with:
|
||||
driver-opts: image=moby/buildkit:v0.11.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
|
@ -32,6 +34,17 @@ jobs:
|
|||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and export to Docker
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
load: true
|
||||
context: ./
|
||||
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
docker run --rm -e "TEST=1" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test
|
||||
|
||||
- name: Get tag as branch name
|
||||
id: tag
|
||||
run: |
|
||||
|
|
6
.github/workflows/docker-ubuntu-image.yml
vendored
6
.github/workflows/docker-ubuntu-image.yml
vendored
|
@ -20,10 +20,10 @@ jobs:
|
|||
submodules: 'recursive'
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
uses: docker/setup-qemu-action@v3.5.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v3.10.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
|
|
14
Changelog.md
14
Changelog.md
|
@ -1,3 +1,17 @@
|
|||
## 2025.03 Update
|
||||
1. New extracurrency behavior introduced, check [GlobalVersions.md](./doc/GlobalVersions.md#version-10)
|
||||
2. Optmization of validation process, in particular CellStorageStat.
|
||||
3. Flag for speeding up broadcasts in various overlays.
|
||||
4. Fixes for static builds for emulator and tonlibjson
|
||||
5. Improving getstats output: adds
|
||||
* Liteserver queries count
|
||||
* Collated/validated blocks count, number of active sessions
|
||||
* Persistent state sizes
|
||||
* Initial sync progress
|
||||
6. Fixes in logging, TON Storage, external message checking, persistent state downloading, UB in tonlib
|
||||
|
||||
Besides the work of the core team, this update is based on the efforts of @Sild from StonFi(UB in tonlib).
|
||||
|
||||
## 2025.02 Update
|
||||
1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersions.md](./doc/GlobalVersions.md)
|
||||
2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries
|
||||
|
|
10
Dockerfile
10
Dockerfile
|
@ -1,6 +1,13 @@
|
|||
FROM ubuntu:22.04 AS builder
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && \
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git ninja-build libsodium-dev libmicrohttpd-dev liblz4-dev pkg-config autoconf automake libtool libjemalloc-dev lsb-release software-properties-common gnupg
|
||||
rm /var/lib/dpkg/info/libc-bin.* && \
|
||||
apt-get clean && \
|
||||
apt-get update && \
|
||||
apt install libc-bin && \
|
||||
apt-get install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git \
|
||||
ninja-build libsodium-dev libmicrohttpd-dev liblz4-dev pkg-config autoconf automake libtool \
|
||||
libjemalloc-dev lsb-release software-properties-common gnupg
|
||||
|
||||
RUN wget https://apt.llvm.org/llvm.sh && \
|
||||
chmod +x llvm.sh && \
|
||||
|
@ -25,6 +32,7 @@ RUN mkdir build && \
|
|||
blockchain-explorer emulator tonlibjson http-proxy adnl-proxy
|
||||
|
||||
FROM ubuntu:22.04
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && \
|
||||
apt-get install -y wget curl libatomic1 openssl libsodium-dev libmicrohttpd-dev liblz4-dev libjemalloc-dev htop \
|
||||
net-tools netcat iptraf-ng jq tcpdump pv plzip && \
|
||||
|
|
|
@ -526,10 +526,12 @@ void CatChainReceiverImpl::start_up() {
|
|||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
root_keys.emplace(get_source(i)->get_hash(), OVERLAY_MAX_ALLOWED_PACKET_SIZE);
|
||||
}
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::create_private_overlay,
|
||||
overlay::OverlayOptions overlay_options;
|
||||
overlay_options.broadcast_speed_multiplier_ = opts_.broadcast_speed_multiplier;
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::create_private_overlay_ex,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_full_id_.clone(), std::move(ids),
|
||||
make_callback(), overlay::OverlayPrivacyRules{0, 0, std::move(root_keys)},
|
||||
R"({ "type": "catchain" })");
|
||||
R"({ "type": "catchain" })", std::move(overlay_options));
|
||||
|
||||
CHECK(root_block_);
|
||||
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
namespace ton {
|
||||
|
||||
// See doc/GlobalVersions.md
|
||||
const int SUPPORTED_VERSION = 9;
|
||||
constexpr int SUPPORTED_VERSION = 10;
|
||||
|
||||
}
|
||||
|
|
|
@ -360,7 +360,6 @@ MsgProcessedUptoCollection::MsgProcessedUptoCollection(ton::ShardIdFull _owner,
|
|||
z.shard = key.get_uint(64);
|
||||
z.mc_seqno = (unsigned)((key + 64).get_uint(32));
|
||||
z.last_inmsg_lt = value.write().fetch_ulong(64);
|
||||
// std::cerr << "ProcessedUpto shard " << std::hex << z.shard << std::dec << std::endl;
|
||||
return value.write().fetch_bits_to(z.last_inmsg_hash) && z.shard && ton::shard_contains(owner.shard, z.shard);
|
||||
});
|
||||
}
|
||||
|
@ -862,8 +861,10 @@ td::Status ShardState::unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_inf
|
|||
out_msg_queue_ =
|
||||
std::make_unique<vm::AugmentedDictionary>(std::move(qinfo.out_queue), 352, block::tlb::aug_OutMsgQueue);
|
||||
if (verbosity >= 3 * 1) {
|
||||
LOG(DEBUG) << "unpacking ProcessedUpto of our previous block " << id_.to_str();
|
||||
block::gen::t_ProcessedInfo.print(std::cerr, qinfo.proc_info);
|
||||
FLOG(DEBUG) {
|
||||
sb << "unpacking ProcessedUpto of our previous block " << id_.to_str();
|
||||
block::gen::t_ProcessedInfo.print(sb, qinfo.proc_info);
|
||||
};
|
||||
}
|
||||
if (!block::gen::t_ProcessedInfo.validate_csr(1024, qinfo.proc_info)) {
|
||||
return td::Status::Error(
|
||||
|
@ -1349,6 +1350,35 @@ bool CurrencyCollection::clamp(const CurrencyCollection& other) {
|
|||
return ok || invalidate();
|
||||
}
|
||||
|
||||
bool CurrencyCollection::check_extra_currency_limit(td::uint32 max_currencies) const {
|
||||
td::uint32 count = 0;
|
||||
return vm::Dictionary{extra, 32}.check_for_each([&](td::Ref<vm::CellSlice>, td::ConstBitPtr, int) {
|
||||
++count;
|
||||
return count <= max_currencies;
|
||||
});
|
||||
}
|
||||
|
||||
bool CurrencyCollection::remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies) {
|
||||
td::uint32 count = 0;
|
||||
vm::Dictionary dict{root, 32};
|
||||
int res = dict.filter([&](const vm::CellSlice& cs, td::ConstBitPtr, int) -> int {
|
||||
++count;
|
||||
if (count > max_currencies) {
|
||||
return -1;
|
||||
}
|
||||
td::RefInt256 val = tlb::t_VarUInteger_32.as_integer(cs);
|
||||
if (val.is_null()) {
|
||||
return -1;
|
||||
}
|
||||
return val->sgn() > 0;
|
||||
});
|
||||
if (res < 0) {
|
||||
return false;
|
||||
}
|
||||
root = dict.get_root_cell();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CurrencyCollection::operator==(const CurrencyCollection& other) const {
|
||||
return is_valid() && other.is_valid() && !td::cmp(grams, other.grams) &&
|
||||
(extra.not_null() == other.extra.not_null()) &&
|
||||
|
|
|
@ -391,6 +391,8 @@ struct CurrencyCollection {
|
|||
CurrencyCollection operator-(CurrencyCollection&& other) const;
|
||||
CurrencyCollection operator-(td::RefInt256 other_grams) const;
|
||||
bool clamp(const CurrencyCollection& other);
|
||||
bool check_extra_currency_limit(td::uint32 max_currencies) const;
|
||||
static bool remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies);
|
||||
bool store(vm::CellBuilder& cb) const;
|
||||
bool store_or_zero(vm::CellBuilder& cb) const;
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
|
|
|
@ -801,7 +801,7 @@ size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells
|
|||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig;
|
||||
size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
|
||||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32
|
||||
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 = SizeLimitsConfig;
|
||||
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 max_msg_extra_currencies:uint32 = SizeLimitsConfig;
|
||||
_ SizeLimitsConfig = ConfigParam 43;
|
||||
|
||||
// key is [ wc:int32 addr:uint256 ]
|
||||
|
|
|
@ -163,8 +163,11 @@ td::Status ConfigInfo::unpack() {
|
|||
}
|
||||
gen::McStateExtra::Record extra_info;
|
||||
if (!tlb::unpack_cell(state_extra_root_, extra_info)) {
|
||||
vm::load_cell_slice(state_extra_root_).print_rec(std::cerr);
|
||||
block::gen::t_McStateExtra.print_ref(std::cerr, state_extra_root_);
|
||||
FLOG(WARNING) {
|
||||
sb << "state extra information is invalid: ";
|
||||
vm::load_cell_slice(state_extra_root_).print_rec(sb);
|
||||
block::gen::t_McStateExtra.print_ref(sb, state_extra_root_);
|
||||
};
|
||||
return td::Status::Error("state extra information is invalid");
|
||||
}
|
||||
gen::ValidatorInfo::Record validator_info;
|
||||
|
@ -1067,7 +1070,6 @@ Ref<McShardHash> ShardConfig::get_shard_hash(ton::ShardIdFull id, bool exact) co
|
|||
ton::ShardIdFull true_id;
|
||||
vm::CellSlice cs;
|
||||
if (get_shard_hash_raw(cs, id, true_id, exact)) {
|
||||
// block::gen::t_ShardDescr.print(std::cerr, vm::CellSlice{cs});
|
||||
return McShardHash::unpack(cs, true_id);
|
||||
} else {
|
||||
return {};
|
||||
|
@ -1637,8 +1639,10 @@ bool ShardConfig::set_shard_info(ton::ShardIdFull shard, Ref<vm::Cell> value) {
|
|||
if (!gen::t_BinTree_ShardDescr.validate_ref(1024, value)) {
|
||||
LOG(ERROR) << "attempting to store an invalid (BinTree ShardDescr) at shard configuration position "
|
||||
<< shard.to_str();
|
||||
gen::t_BinTree_ShardDescr.print_ref(std::cerr, value);
|
||||
vm::load_cell_slice(value).print_rec(std::cerr);
|
||||
FLOG(WARNING) {
|
||||
gen::t_BinTree_ShardDescr.print_ref(sb, value);
|
||||
vm::load_cell_slice(value).print_rec(sb);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
auto root = shard_hashes_dict_->lookup_ref(td::BitArray<32>{shard.workchain});
|
||||
|
@ -1956,6 +1960,7 @@ td::Result<SizeLimitsConfig> Config::do_get_size_limits_config(td::Ref<vm::CellS
|
|||
limits.max_acc_state_cells = rec.max_acc_state_cells;
|
||||
limits.max_acc_public_libraries = rec.max_acc_public_libraries;
|
||||
limits.defer_out_queue_size_limit = rec.defer_out_queue_size_limit;
|
||||
limits.max_msg_extra_currencies = rec.max_msg_extra_currencies;
|
||||
};
|
||||
gen::SizeLimitsConfig::Record_size_limits_config rec_v1;
|
||||
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2;
|
||||
|
|
|
@ -397,6 +397,7 @@ struct SizeLimitsConfig {
|
|||
td::uint32 max_acc_state_bits = (1 << 16) * 1023;
|
||||
td::uint32 max_acc_public_libraries = 256;
|
||||
td::uint32 defer_out_queue_size_limit = 256;
|
||||
td::uint32 max_msg_extra_currencies = 2;
|
||||
};
|
||||
|
||||
struct CatchainValidatorsConfig {
|
||||
|
|
|
@ -138,7 +138,6 @@ bool OutputQueueMerger::add_root(int src, Ref<vm::Cell> outmsg_root) {
|
|||
if (outmsg_root.is_null()) {
|
||||
return true;
|
||||
}
|
||||
//block::gen::HashmapAug{352, block::gen::t_EnqueuedMsg, block::gen::t_uint64}.print_ref(std::cerr, outmsg_root);
|
||||
auto kv = std::make_unique<MsgKeyValue>(src, std::move(outmsg_root));
|
||||
if (kv->replace_by_prefix(common_pfx.cbits(), common_pfx_len)) {
|
||||
heap.push_back(std::move(kv));
|
||||
|
|
|
@ -446,8 +446,10 @@ bool Account::unpack(Ref<vm::CellSlice> shard_account, ton::UnixTime now, bool s
|
|||
return false;
|
||||
}
|
||||
if (verbosity > 2) {
|
||||
shard_account->print_rec(std::cerr, 2);
|
||||
block::gen::t_ShardAccount.print(std::cerr, *shard_account);
|
||||
FLOG(INFO) {
|
||||
shard_account->print_rec(sb, 2);
|
||||
block::gen::t_ShardAccount.print(sb, shard_account);
|
||||
};
|
||||
}
|
||||
block::gen::ShardAccount::Record acc_info;
|
||||
if (!(block::tlb::t_ShardAccount.validate_csr(shard_account) && tlb::unpack_exact(shard_account.write(), acc_info))) {
|
||||
|
@ -737,9 +739,11 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig*
|
|||
return false;
|
||||
}
|
||||
if (verbosity > 2) {
|
||||
fprintf(stderr, "unpacking inbound message for a new transaction: ");
|
||||
block::gen::t_Message_Any.print_ref(std::cerr, in_msg);
|
||||
load_cell_slice(in_msg).print_rec(std::cerr);
|
||||
FLOG(INFO) {
|
||||
sb << "unpacking inbound message for a new transaction: ";
|
||||
block::gen::t_Message_Any.print_ref(sb, in_msg);
|
||||
load_cell_slice(in_msg).print_rec(sb);
|
||||
};
|
||||
}
|
||||
auto cs = vm::load_cell_slice(in_msg);
|
||||
int tag = block::gen::t_CommonMsgInfo.get_tag(cs);
|
||||
|
@ -1550,11 +1554,13 @@ bool Transaction::run_precompiled_contract(const ComputePhaseConfig& cfg, precom
|
|||
cp.actions = impl.get_c5();
|
||||
int out_act_num = output_actions_count(cp.actions);
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "new smart contract data: ";
|
||||
bool can_be_special = true;
|
||||
load_cell_slice_special(cp.new_data, can_be_special).print_rec(std::cerr);
|
||||
std::cerr << "output actions: ";
|
||||
block::gen::OutList{out_act_num}.print_ref(std::cerr, cp.actions);
|
||||
FLOG(INFO) {
|
||||
sb << "new smart contract data: ";
|
||||
bool can_be_special = true;
|
||||
load_cell_slice_special(cp.new_data, can_be_special).print_rec(sb);
|
||||
sb << "output actions: ";
|
||||
block::gen::OutList{out_act_num}.print_ref(sb, cp.actions);
|
||||
};
|
||||
}
|
||||
}
|
||||
cp.mode = 0;
|
||||
|
@ -1619,7 +1625,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
if (in_msg_state.not_null()) {
|
||||
LOG(DEBUG) << "HASH(in_msg_state) = " << in_msg_state->get_hash().bits().to_hex(256)
|
||||
<< ", account_state_hash = " << account.state_hash.to_hex();
|
||||
// vm::load_cell_slice(in_msg_state).print_rec(std::cerr);
|
||||
} else {
|
||||
LOG(DEBUG) << "in_msg_state is null";
|
||||
}
|
||||
|
@ -1775,11 +1780,13 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
cp.actions = vm.get_committed_state().c5; // c5 -> action list
|
||||
int out_act_num = output_actions_count(cp.actions);
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "new smart contract data: ";
|
||||
bool can_be_special = true;
|
||||
load_cell_slice_special(cp.new_data, can_be_special).print_rec(std::cerr);
|
||||
std::cerr << "output actions: ";
|
||||
block::gen::OutList{out_act_num}.print_ref(std::cerr, cp.actions);
|
||||
FLOG(INFO) {
|
||||
sb << "new smart contract data: ";
|
||||
bool can_be_special = true;
|
||||
load_cell_slice_special(cp.new_data, can_be_special).print_rec(sb);
|
||||
sb << "output actions: ";
|
||||
block::gen::OutList{out_act_num}.print_ref(sb, cp.actions);
|
||||
};
|
||||
}
|
||||
}
|
||||
cp.mode = 0;
|
||||
|
@ -1993,9 +2000,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
ap.remaining_balance += ap.reserved_balance;
|
||||
CHECK(ap.remaining_balance.is_valid());
|
||||
if (ap.acc_delete_req) {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
CHECK(cfg.extra_currency_v2 ? ap.remaining_balance.grams->sgn() == 0 : ap.remaining_balance.is_zero());
|
||||
ap.acc_status_change = ActionPhase::acst_deleted;
|
||||
acc_status = Account::acc_deleted;
|
||||
acc_status = (ap.remaining_balance.is_zero() ? Account::acc_deleted : Account::acc_uninit);
|
||||
was_deleted = true;
|
||||
}
|
||||
ap.success = true;
|
||||
|
@ -2465,6 +2472,20 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
LOG(DEBUG) << "invalid destination address in a proposed outbound message";
|
||||
return check_skip_invalid(36); // invalid destination address
|
||||
}
|
||||
if (cfg.extra_currency_v2) {
|
||||
CurrencyCollection value;
|
||||
if (!value.unpack(info.value)) {
|
||||
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message";
|
||||
return check_skip_invalid(37); // invalid value:CurrencyCollection
|
||||
}
|
||||
if (!CurrencyCollection::remove_zero_extra_currencies(value.extra, cfg.size_limits.max_msg_extra_currencies)) {
|
||||
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message: too many currencies (max "
|
||||
<< cfg.size_limits.max_msg_extra_currencies << ")";
|
||||
// Dict should be valid, since it was checked in t_OutListNode.validate_ref, so error here means limit exceeded
|
||||
return check_skip_invalid(41); // invalid value:CurrencyCollection : too many extra currencies
|
||||
}
|
||||
info.value = value.pack();
|
||||
}
|
||||
|
||||
// fetch message pricing info
|
||||
const MsgPrices& msg_prices = cfg.fetch_msg_prices(to_mc || account.is_masterchain());
|
||||
|
@ -2517,7 +2538,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
};
|
||||
add_used_storage(msg.init, 3); // message init
|
||||
add_used_storage(msg.body, 3); // message body (the root cell itself is not counted)
|
||||
if (!ext_msg) {
|
||||
if (!ext_msg && !cfg.extra_currency_v2) {
|
||||
add_used_storage(info.value->prefetch_ref(), 0);
|
||||
}
|
||||
auto collect_fine = [&] {
|
||||
|
@ -2588,11 +2609,19 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
|
||||
if (act_rec.mode & 0x80) {
|
||||
// attach all remaining balance to this message
|
||||
req = ap.remaining_balance;
|
||||
if (cfg.extra_currency_v2) {
|
||||
req.grams = ap.remaining_balance.grams;
|
||||
} else {
|
||||
req = ap.remaining_balance;
|
||||
}
|
||||
act_rec.mode &= ~1; // pay fees from attached value
|
||||
} else if (act_rec.mode & 0x40) {
|
||||
// attach all remaining balance of the inbound message (in addition to the original value)
|
||||
req += msg_balance_remaining;
|
||||
if (cfg.extra_currency_v2) {
|
||||
req.grams += msg_balance_remaining.grams;
|
||||
} else {
|
||||
req += msg_balance_remaining;
|
||||
}
|
||||
if (!(act_rec.mode & 1)) {
|
||||
req -= ap.action_fine;
|
||||
if (compute_phase) {
|
||||
|
@ -2632,6 +2661,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
return check_skip_invalid(37); // not enough grams
|
||||
}
|
||||
|
||||
if (cfg.extra_currency_v2 && !req.check_extra_currency_limit(cfg.size_limits.max_msg_extra_currencies)) {
|
||||
LOG(DEBUG) << "too many extra currencies in the message : max " << cfg.size_limits.max_msg_extra_currencies;
|
||||
return check_skip_invalid(41); // to many extra currencies
|
||||
}
|
||||
|
||||
Ref<vm::Cell> new_extra;
|
||||
|
||||
if (!block::sub_extra_currency(ap.remaining_balance.extra, req.extra, new_extra)) {
|
||||
|
@ -2673,7 +2707,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
|
||||
// clear msg_balance_remaining if it has been used
|
||||
if (act_rec.mode & 0xc0) {
|
||||
msg_balance_remaining.set_zero();
|
||||
if (cfg.extra_currency_v2) {
|
||||
msg_balance_remaining.grams = td::zero_refint();
|
||||
} else {
|
||||
msg_balance_remaining.set_zero();
|
||||
}
|
||||
}
|
||||
|
||||
// update balance
|
||||
|
@ -2725,14 +2763,18 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
}
|
||||
if (!block::gen::t_Message_Any.validate_ref(new_msg)) {
|
||||
LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to automated check";
|
||||
block::gen::t_Message_Any.print_ref(std::cerr, new_msg);
|
||||
vm::load_cell_slice(new_msg).print_rec(std::cerr);
|
||||
FLOG(INFO) {
|
||||
block::gen::t_Message_Any.print_ref(sb, new_msg);
|
||||
vm::load_cell_slice(new_msg).print_rec(sb);
|
||||
};
|
||||
collect_fine();
|
||||
return -1;
|
||||
}
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "converted outbound message: ";
|
||||
block::gen::t_Message_Any.print_ref(std::cerr, new_msg);
|
||||
FLOG(INFO) {
|
||||
sb << "converted outbound message: ";
|
||||
block::gen::t_Message_Any.print_ref(sb, new_msg);
|
||||
};
|
||||
}
|
||||
|
||||
ap.msgs_created++;
|
||||
|
@ -2743,8 +2785,13 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
ap.total_fwd_fees += fees_total;
|
||||
|
||||
if ((act_rec.mode & 0xa0) == 0xa0) {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
ap.acc_delete_req = ap.reserved_balance.is_zero();
|
||||
if (cfg.extra_currency_v2) {
|
||||
CHECK(ap.remaining_balance.grams->sgn() == 0);
|
||||
ap.acc_delete_req = ap.reserved_balance.grams->sgn() == 0;
|
||||
} else {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
ap.acc_delete_req = ap.reserved_balance.is_zero();
|
||||
}
|
||||
}
|
||||
|
||||
ap.tot_msg_bits += sstat.bits + new_msg_bits;
|
||||
|
@ -3015,7 +3062,8 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
|
|||
bp.fwd_fees -= bp.fwd_fees_collected;
|
||||
total_fees += td::make_refint(bp.fwd_fees_collected);
|
||||
// serialize outbound message
|
||||
info.created_lt = end_lt++;
|
||||
info.created_lt = start_lt + 1 + out_msgs.size();
|
||||
end_lt++;
|
||||
info.created_at = now;
|
||||
vm::CellBuilder cb;
|
||||
CHECK(cb.store_long_bool(5, 4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
|
@ -3045,8 +3093,10 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
|
|||
}
|
||||
CHECK(cb.finalize_to(bp.out_msg));
|
||||
if (verbosity > 2) {
|
||||
LOG(INFO) << "generated bounced message: ";
|
||||
block::gen::t_Message_Any.print_ref(std::cerr, bp.out_msg);
|
||||
FLOG(INFO) {
|
||||
sb << "generated bounced message: ";
|
||||
block::gen::t_Message_Any.print_ref(sb, bp.out_msg);
|
||||
};
|
||||
}
|
||||
out_msgs.push_back(bp.out_msg);
|
||||
bp.ok = true;
|
||||
|
@ -3094,6 +3144,7 @@ bool Account::store_acc_status(vm::CellBuilder& cb, int acc_status) const {
|
|||
* Tries to update the storage statistics based on the old storage statistics and old account state without fully recomputing it.
|
||||
*
|
||||
* It succeeds if only root cell of AccountStorage is changed.
|
||||
* old_cs and new_cell are AccountStorage without extra currencies (if global_version >= 10).
|
||||
*
|
||||
* @param old_stat The old storage statistics.
|
||||
* @param old_cs The old AccountStorage.
|
||||
|
@ -3127,13 +3178,48 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
|
|||
return new_stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes extra currencies dict from AccountStorage.
|
||||
*
|
||||
* This is used for computing account storage stats.
|
||||
*
|
||||
* @param storage_cs AccountStorage as CellSlice.
|
||||
*
|
||||
* @returns AccountStorage without extra currencies as Cell.
|
||||
*/
|
||||
static td::Ref<vm::Cell> storage_without_extra_currencies(td::Ref<vm::CellSlice> storage_cs) {
|
||||
block::gen::AccountStorage::Record rec;
|
||||
if (!block::gen::csr_unpack(storage_cs, rec)) {
|
||||
LOG(ERROR) << "failed to unpack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
if (rec.balance->size_refs() > 0) {
|
||||
block::gen::CurrencyCollection::Record balance;
|
||||
if (!block::gen::csr_unpack(rec.balance, balance)) {
|
||||
LOG(ERROR) << "failed to unpack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
balance.other = vm::CellBuilder{}.store_zeroes(1).as_cellslice_ref();
|
||||
if (!block::gen::csr_pack(rec.balance, balance)) {
|
||||
LOG(ERROR) << "failed to pack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
td::Ref<vm::Cell> cell;
|
||||
if (!block::gen::pack_cell(cell, rec)) {
|
||||
LOG(ERROR) << "failed to pack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
namespace transaction {
|
||||
/**
|
||||
* Computes the new state of the account.
|
||||
*
|
||||
* @returns True if the state computation is successful, false otherwise.
|
||||
*/
|
||||
bool Transaction::compute_state() {
|
||||
bool Transaction::compute_state(const SerializeConfig& cfg) {
|
||||
if (new_total_state.not_null()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3167,11 +3253,13 @@ bool Transaction::compute_state() {
|
|||
auto frozen_state = cb2.finalize();
|
||||
frozen_hash = frozen_state->get_hash().bits();
|
||||
if (verbosity >= 3 * 1) { // !!!DEBUG!!!
|
||||
std::cerr << "freezing state of smart contract: ";
|
||||
block::gen::t_StateInit.print_ref(std::cerr, frozen_state);
|
||||
CHECK(block::gen::t_StateInit.validate_ref(frozen_state));
|
||||
CHECK(block::tlb::t_StateInit.validate_ref(frozen_state));
|
||||
std::cerr << "with hash " << frozen_hash.to_hex() << std::endl;
|
||||
FLOG(INFO) {
|
||||
sb << "freezing state of smart contract: ";
|
||||
block::gen::t_StateInit.print_ref(sb, frozen_state);
|
||||
CHECK(block::gen::t_StateInit.validate_ref(frozen_state));
|
||||
CHECK(block::tlb::t_StateInit.validate_ref(frozen_state));
|
||||
sb << "with hash " << frozen_hash.to_hex();
|
||||
};
|
||||
}
|
||||
}
|
||||
new_code.clear();
|
||||
|
@ -3203,13 +3291,27 @@ bool Transaction::compute_state() {
|
|||
new_inner_state.clear();
|
||||
}
|
||||
vm::CellStorageStat& stats = new_storage_stat;
|
||||
auto new_stats = try_update_storage_stat(account.storage_stat, account.storage, storage);
|
||||
td::Ref<vm::CellSlice> old_storage_for_stat = account.storage;
|
||||
td::Ref<vm::Cell> new_storage_for_stat = storage;
|
||||
if (cfg.extra_currency_v2) {
|
||||
new_storage_for_stat = storage_without_extra_currencies(new_storage);
|
||||
if (new_storage_for_stat.is_null()) {
|
||||
return false;
|
||||
}
|
||||
if (old_storage_for_stat.not_null()) {
|
||||
old_storage_for_stat = vm::load_cell_slice_ref(storage_without_extra_currencies(old_storage_for_stat));
|
||||
if (old_storage_for_stat.is_null()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto new_stats = try_update_storage_stat(account.storage_stat, old_storage_for_stat, storage);
|
||||
if (new_stats) {
|
||||
stats = new_stats.unwrap();
|
||||
} else {
|
||||
TD_PERF_COUNTER(transaction_storage_stat_b);
|
||||
td::Timer timer;
|
||||
stats.add_used_storage(Ref<vm::Cell>(storage)).ensure();
|
||||
stats.add_used_storage(new_storage_for_stat).ensure();
|
||||
if (timer.elapsed() > 0.1) {
|
||||
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
|
||||
}
|
||||
|
@ -3229,8 +3331,10 @@ bool Transaction::compute_state() {
|
|||
CHECK(cb.append_data_cell_bool(std::move(storage)));
|
||||
new_total_state = cb.finalize();
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "new account state: ";
|
||||
block::gen::t_Account.print_ref(std::cerr, new_total_state);
|
||||
FLOG(INFO) {
|
||||
sb << "new account state: ";
|
||||
block::gen::t_Account.print_ref(sb, new_total_state);
|
||||
};
|
||||
}
|
||||
CHECK(block::tlb::t_Account.validate_ref(new_total_state));
|
||||
return true;
|
||||
|
@ -3243,11 +3347,11 @@ bool Transaction::compute_state() {
|
|||
*
|
||||
* @returns True if the serialization is successful, False otherwise.
|
||||
*/
|
||||
bool Transaction::serialize() {
|
||||
bool Transaction::serialize(const SerializeConfig& cfg) {
|
||||
if (root.not_null()) {
|
||||
return true;
|
||||
}
|
||||
if (!compute_state()) {
|
||||
if (!compute_state(cfg)) {
|
||||
return false;
|
||||
}
|
||||
vm::Dictionary dict{15};
|
||||
|
@ -3322,22 +3426,28 @@ bool Transaction::serialize() {
|
|||
return false;
|
||||
}
|
||||
if (verbosity >= 3 * 1) {
|
||||
std::cerr << "new transaction: ";
|
||||
block::gen::t_Transaction.print_ref(std::cerr, root);
|
||||
vm::load_cell_slice(root).print_rec(std::cerr);
|
||||
FLOG(INFO) {
|
||||
sb << "new transaction: ";
|
||||
block::gen::t_Transaction.print_ref(sb, root);
|
||||
vm::load_cell_slice(root).print_rec(sb);
|
||||
};
|
||||
}
|
||||
|
||||
if (!block::gen::t_Transaction.validate_ref(4096, root)) {
|
||||
LOG(ERROR) << "newly-generated transaction failed to pass automated validation:";
|
||||
vm::load_cell_slice(root).print_rec(std::cerr);
|
||||
block::gen::t_Transaction.print_ref(std::cerr, root);
|
||||
FLOG(INFO) {
|
||||
vm::load_cell_slice(root).print_rec(sb);
|
||||
block::gen::t_Transaction.print_ref(sb, root);
|
||||
};
|
||||
root.clear();
|
||||
return false;
|
||||
}
|
||||
if (!block::tlb::t_Transaction.validate_ref(4096, root)) {
|
||||
LOG(ERROR) << "newly-generated transaction failed to pass hand-written validation:";
|
||||
vm::load_cell_slice(root).print_rec(std::cerr);
|
||||
block::gen::t_Transaction.print_ref(std::cerr, root);
|
||||
FLOG(INFO) {
|
||||
vm::load_cell_slice(root).print_rec(sb);
|
||||
block::gen::t_Transaction.print_ref(sb, root);
|
||||
};
|
||||
root.clear();
|
||||
return false;
|
||||
}
|
||||
|
@ -3707,6 +3817,7 @@ bool Account::libraries_changed() const {
|
|||
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
|
||||
* @param compute_phase_cfg Pointer to store the compute phase configuration.
|
||||
* @param action_phase_cfg Pointer to store the action phase configuration.
|
||||
* @param serialize_cfg Pointer to store the serialize phase configuration.
|
||||
* @param masterchain_create_fee Pointer to store the masterchain create fee.
|
||||
* @param basechain_create_fee Pointer to store the basechain create fee.
|
||||
* @param wc The workchain ID.
|
||||
|
@ -3715,15 +3826,15 @@ bool Account::libraries_changed() const {
|
|||
td::Status FetchConfigParams::fetch_config_params(
|
||||
const block::ConfigInfo& config, Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg,
|
||||
ActionPhaseConfig* action_phase_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now) {
|
||||
ActionPhaseConfig* action_phase_cfg, SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now) {
|
||||
auto prev_blocks_info = config.get_prev_blocks_info();
|
||||
if (prev_blocks_info.is_error()) {
|
||||
return prev_blocks_info.move_as_error_prefix(
|
||||
td::Status::Error(-668, "cannot fetch prev blocks info from masterchain configuration: "));
|
||||
}
|
||||
return fetch_config_params(config, prev_blocks_info.move_as_ok(), old_mparams, storage_prices, storage_phase_cfg,
|
||||
rand_seed, compute_phase_cfg, action_phase_cfg, masterchain_create_fee,
|
||||
rand_seed, compute_phase_cfg, action_phase_cfg, serialize_cfg, masterchain_create_fee,
|
||||
basechain_create_fee, wc, now);
|
||||
}
|
||||
|
||||
|
@ -3738,6 +3849,7 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
|
||||
* @param compute_phase_cfg Pointer to store the compute phase configuration.
|
||||
* @param action_phase_cfg Pointer to store the action phase configuration.
|
||||
* @param serialize_cfg Pointer to store the serialize phase configuration.
|
||||
* @param masterchain_create_fee Pointer to store the masterchain create fee.
|
||||
* @param basechain_create_fee Pointer to store the basechain create fee.
|
||||
* @param wc The workchain ID.
|
||||
|
@ -3747,8 +3859,8 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
const block::Config& config, td::Ref<vm::Tuple> prev_blocks_info, Ref<vm::Cell>* old_mparams,
|
||||
std::vector<block::StoragePrices>* storage_prices, StoragePhaseConfig* storage_phase_cfg,
|
||||
td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee, ton::WorkchainId wc,
|
||||
ton::UnixTime now) {
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now) {
|
||||
*old_mparams = config.get_config_param(9);
|
||||
{
|
||||
auto res = config.get_storage_prices();
|
||||
|
@ -3820,6 +3932,10 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8;
|
||||
action_phase_cfg->reserve_extra_enabled = config.get_global_version() >= 9;
|
||||
action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr;
|
||||
action_phase_cfg->extra_currency_v2 = config.get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
serialize_cfg->extra_currency_v2 = config.get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
// fetch block_grams_created
|
||||
|
|
|
@ -170,12 +170,17 @@ struct ActionPhaseConfig {
|
|||
bool message_skip_enabled{false};
|
||||
bool disable_custom_fess{false};
|
||||
bool reserve_extra_enabled{false};
|
||||
bool extra_currency_v2{false};
|
||||
td::optional<td::Bits256> mc_blackhole_addr;
|
||||
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
|
||||
return is_masterchain ? fwd_mc : fwd_std;
|
||||
}
|
||||
};
|
||||
|
||||
struct SerializeConfig {
|
||||
bool extra_currency_v2{false};
|
||||
};
|
||||
|
||||
struct CreditPhase {
|
||||
td::RefInt256 due_fees_collected;
|
||||
block::CurrencyCollection credit;
|
||||
|
@ -389,8 +394,8 @@ struct Transaction {
|
|||
bool prepare_action_phase(const ActionPhaseConfig& cfg);
|
||||
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
|
||||
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
|
||||
bool compute_state();
|
||||
bool serialize();
|
||||
bool compute_state(const SerializeConfig& cfg);
|
||||
bool serialize(const SerializeConfig& cfg);
|
||||
td::uint64 gas_used() const {
|
||||
return compute_phase ? compute_phase->gas_used : 0;
|
||||
}
|
||||
|
@ -428,14 +433,14 @@ struct FetchConfigParams {
|
|||
std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
|
||||
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now);
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
|
||||
static td::Status fetch_config_params(const block::Config& config, Ref<vm::Tuple> prev_blocks_info,
|
||||
Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
|
||||
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now);
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
|
||||
};
|
||||
|
||||
} // namespace block
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Standard library for Tolk (LGPL licence).
|
||||
// It contains common functions that are available out of the box, the user doesn't have to import anything.
|
||||
// More specific functions are required to be imported explicitly, like "@stdlib/tvm-dicts".
|
||||
tolk 0.8
|
||||
tolk 0.9
|
||||
|
||||
/**
|
||||
Tuple manipulation primitives.
|
||||
|
@ -139,7 +139,7 @@ fun getMyOriginalBalance(): int
|
|||
/// `int` — balance in nanotoncoins;
|
||||
/// `cell` — a dictionary with 32-bit keys representing the balance of "extra currencies".
|
||||
@pure
|
||||
fun getMyOriginalBalanceWithExtraCurrencies(): [int, cell]
|
||||
fun getMyOriginalBalanceWithExtraCurrencies(): [int, cell?]
|
||||
asm "BALANCE";
|
||||
|
||||
/// Returns the logical time of the current transaction.
|
||||
|
@ -154,7 +154,7 @@ fun getCurrentBlockLogicalTime(): int
|
|||
|
||||
/// Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value.
|
||||
@pure
|
||||
fun getBlockchainConfigParam(x: int): cell
|
||||
fun getBlockchainConfigParam(x: int): cell?
|
||||
asm "CONFIGOPTPARAM";
|
||||
|
||||
/// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later.
|
||||
|
@ -291,7 +291,7 @@ fun calculateSliceSizeStrict(s: slice, maxCells: int): (int, int, int)
|
|||
/// otherwise the returned value is one plus the maximum of depths of cells referred to from [c].
|
||||
/// If [c] is a `null` instead of a cell, returns zero.
|
||||
@pure
|
||||
fun getCellDepth(c: cell): int
|
||||
fun getCellDepth(c: cell?): int
|
||||
asm "CDEPTH";
|
||||
|
||||
/// Returns the depth of `slice` [s].
|
||||
|
@ -417,12 +417,12 @@ fun getLastBits(self: slice, len: int): slice
|
|||
/// Loads a dictionary (TL HashMapE structure, represented as TVM cell) from a slice.
|
||||
/// Returns `null` if `nothing` constructor is used.
|
||||
@pure
|
||||
fun loadDict(mutate self: slice): cell
|
||||
fun loadDict(mutate self: slice): cell?
|
||||
asm( -> 1 0) "LDDICT";
|
||||
|
||||
/// Preloads a dictionary (cell) from a slice.
|
||||
@pure
|
||||
fun preloadDict(self: slice): cell
|
||||
fun preloadDict(self: slice): cell?
|
||||
asm "PLDDICT";
|
||||
|
||||
/// Loads a dictionary as [loadDict], but returns only the remainder of the slice.
|
||||
|
@ -433,12 +433,12 @@ fun skipDict(mutate self: slice): self
|
|||
/// Loads (Maybe ^Cell) from a slice.
|
||||
/// In other words, loads 1 bit: if it's true, loads the first ref, otherwise returns `null`.
|
||||
@pure
|
||||
fun loadMaybeRef(mutate self: slice): cell
|
||||
fun loadMaybeRef(mutate self: slice): cell?
|
||||
asm( -> 1 0) "LDOPTREF";
|
||||
|
||||
/// Preloads (Maybe ^Cell) from a slice.
|
||||
@pure
|
||||
fun preloadMaybeRef(self: slice): cell
|
||||
fun preloadMaybeRef(self: slice): cell?
|
||||
asm "PLDOPTREF";
|
||||
|
||||
/// Loads (Maybe ^Cell), but returns only the remainder of the slice.
|
||||
|
@ -497,13 +497,13 @@ fun storeBool(mutate self: builder, x: bool): self
|
|||
/// Stores dictionary (represented by TVM `cell` or `null`) into a builder.
|
||||
/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise.
|
||||
@pure
|
||||
fun storeDict(mutate self: builder, c: cell): self
|
||||
fun storeDict(mutate self: builder, c: cell?): self
|
||||
asm(c self) "STDICT";
|
||||
|
||||
/// Stores (Maybe ^Cell) into a builder.
|
||||
/// In other words, if cell is `null`, store '0' bit; otherwise, store '1' and a ref to [c].
|
||||
@pure
|
||||
fun storeMaybeRef(mutate self: builder, c: cell): self
|
||||
fun storeMaybeRef(mutate self: builder, c: cell?): self
|
||||
asm(c self) "STOPTREF";
|
||||
|
||||
/// Concatenates two builders.
|
||||
|
@ -661,7 +661,7 @@ fun reserveToncoinsOnBalance(nanoTonCoins: int, reserveMode: int): void
|
|||
|
||||
/// Similar to [reserveToncoinsOnBalance], but also accepts a dictionary extraAmount (represented by a cell or null)
|
||||
/// with extra currencies. In this way currencies other than Toncoin can be reserved.
|
||||
fun reserveExtraCurrenciesOnBalance(nanoTonCoins: int, extraAmount: cell, reserveMode: int): void
|
||||
fun reserveExtraCurrenciesOnBalance(nanoTonCoins: int, extraAmount: cell?, reserveMode: int): void
|
||||
asm "RAWRESERVEX";
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.8
|
||||
tolk 0.9
|
||||
|
||||
/**
|
||||
Gas and payment related primitives.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.8
|
||||
tolk 0.9
|
||||
|
||||
/**
|
||||
Lisp-style lists are nested 2-elements tuples: `(1, (2, (3, null)))` represents list `[1, 2, 3]`.
|
||||
|
@ -14,17 +14,18 @@ fun createEmptyList(): tuple
|
|||
/// Adds an element to the beginning of lisp-style list.
|
||||
/// Note, that it does not mutate the list: instead, it returns a new one (it's a lisp pattern).
|
||||
@pure
|
||||
fun listPrepend<X>(head: X, tail: tuple): tuple
|
||||
fun listPrepend<X>(head: X, tail: tuple?): tuple
|
||||
asm "CONS";
|
||||
|
||||
/// Extracts the head and the tail of lisp-style list.
|
||||
@pure
|
||||
fun listSplit<X>(list: tuple): (X, tuple)
|
||||
fun listSplit<X>(list: tuple): (X, tuple?)
|
||||
asm "UNCONS";
|
||||
|
||||
/// Extracts the tail and the head of lisp-style list.
|
||||
/// After extracting the last element, tuple is assigned to null.
|
||||
@pure
|
||||
fun listNext<X>(mutate self: tuple): X
|
||||
fun listNext<X>(mutate self: tuple?): X
|
||||
asm( -> 1 0) "UNCONS";
|
||||
|
||||
/// Returns the head of lisp-style list.
|
||||
|
@ -34,5 +35,5 @@ fun listGetHead<X>(list: tuple): X
|
|||
|
||||
/// Returns the tail of lisp-style list.
|
||||
@pure
|
||||
fun listGetTail(list: tuple): tuple
|
||||
fun listGetTail(list: tuple): tuple?
|
||||
asm "CDR";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.8
|
||||
tolk 0.9
|
||||
|
||||
/**
|
||||
Dictionaries are represented as `cell` data type (cells can store anything, dicts in particular).
|
||||
|
@ -9,288 +9,289 @@ tolk 0.8
|
|||
- uDict* - dicts with unsigned integer keys
|
||||
- sDict* - dicts with arbitrary slice keys
|
||||
When accessing a dict element, you should not only provide a key, but provide keyLen,
|
||||
since for optimization, for optimization, key length is not stored in the dictionary itself.
|
||||
since for optimization, key length is not stored in the dictionary itself.
|
||||
Every dictionary object (`self` parameter) can be null. TVM NULL is essentially "empty dictionary".
|
||||
*/
|
||||
|
||||
/// Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL
|
||||
@pure
|
||||
fun createEmptyDict(): cell
|
||||
fun createEmptyDict(): cell?
|
||||
asm "NEWDICT";
|
||||
|
||||
/// Checks whether a dictionary is empty.
|
||||
@pure
|
||||
fun dictIsEmpty(self: cell): bool
|
||||
fun dictIsEmpty(self: cell?): bool
|
||||
asm "DICTEMPTY";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGet(self: cell, keyLen: int, key: int): (slice, bool)
|
||||
fun iDictGet(self: cell?, keyLen: int, key: int): (slice?, bool)
|
||||
asm(key self keyLen) "DICTIGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictGet(self: cell, keyLen: int, key: int): (slice, bool)
|
||||
fun uDictGet(self: cell?, keyLen: int, key: int): (slice?, bool)
|
||||
asm(key self keyLen) "DICTUGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictGet(self: cell, keyLen: int, key: slice): (slice, bool)
|
||||
fun sDictGet(self: cell?, keyLen: int, key: slice): (slice?, bool)
|
||||
asm(key self keyLen) "DICTGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||
fun iDictSet(mutate self: cell?, keyLen: int, key: int, value: slice): void
|
||||
asm(value key self keyLen) "DICTISET";
|
||||
|
||||
@pure
|
||||
fun uDictSet(mutate self: cell, keyLen: int, key: int, value: slice): void
|
||||
fun uDictSet(mutate self: cell?, keyLen: int, key: int, value: slice): void
|
||||
asm(value key self keyLen) "DICTUSET";
|
||||
|
||||
@pure
|
||||
fun sDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): void
|
||||
fun sDictSet(mutate self: cell?, keyLen: int, key: slice, value: slice): void
|
||||
asm(value key self keyLen) "DICTSET";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||
fun iDictSetRef(mutate self: cell?, keyLen: int, key: int, value: cell): void
|
||||
asm(value key self keyLen) "DICTISETREF";
|
||||
|
||||
@pure
|
||||
fun uDictSetRef(mutate self: cell, keyLen: int, key: int, value: cell): void
|
||||
fun uDictSetRef(mutate self: cell?, keyLen: int, key: int, value: cell): void
|
||||
asm(value key self keyLen) "DICTUSETREF";
|
||||
|
||||
@pure
|
||||
fun sDictSetRef(mutate self: cell, keyLen: int, key: slice, value: cell): void
|
||||
fun sDictSetRef(mutate self: cell?, keyLen: int, key: slice, value: cell): void
|
||||
asm(value key self keyLen) "DICTSETREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): bool
|
||||
fun iDictSetIfNotExists(mutate self: cell?, keyLen: int, key: int, value: slice): bool
|
||||
asm(value key self keyLen) "DICTIADD";
|
||||
|
||||
@pure
|
||||
fun uDictSetIfNotExists(mutate self: cell, keyLen: int, key: int, value: slice): bool
|
||||
fun uDictSetIfNotExists(mutate self: cell?, keyLen: int, key: int, value: slice): bool
|
||||
asm(value key self keyLen) "DICTUADD";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): bool
|
||||
fun iDictSetIfExists(mutate self: cell?, keyLen: int, key: int, value: slice): bool
|
||||
asm(value key self keyLen) "DICTIREPLACE";
|
||||
|
||||
@pure
|
||||
fun uDictSetIfExists(mutate self: cell, keyLen: int, key: int, value: slice): bool
|
||||
fun uDictSetIfExists(mutate self: cell?, keyLen: int, key: int, value: slice): bool
|
||||
asm(value key self keyLen) "DICTUREPLACE";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetRef(self: cell, keyLen: int, key: int): (cell, bool)
|
||||
fun iDictGetRef(self: cell?, keyLen: int, key: int): (cell?, bool)
|
||||
asm(key self keyLen) "DICTIGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictGetRef(self: cell, keyLen: int, key: int): (cell, bool)
|
||||
fun uDictGetRef(self: cell?, keyLen: int, key: int): (cell?, bool)
|
||||
asm(key self keyLen) "DICTUGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictGetRef(self: cell, keyLen: int, key: slice): (cell, bool)
|
||||
fun sDictGetRef(self: cell?, keyLen: int, key: slice): (cell?, bool)
|
||||
asm(key self keyLen) "DICTGETREF" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||
fun iDictGetRefOrNull(self: cell?, keyLen: int, key: int): cell?
|
||||
asm(key self keyLen) "DICTIGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun uDictGetRefOrNull(self: cell, keyLen: int, key: int): cell
|
||||
fun uDictGetRefOrNull(self: cell?, keyLen: int, key: int): cell?
|
||||
asm(key self keyLen) "DICTUGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun sDictGetRefOrNull(self: cell, keyLen: int, key: slice): cell
|
||||
fun sDictGetRefOrNull(self: cell?, keyLen: int, key: slice): cell?
|
||||
asm(key self keyLen) "DICTGETOPTREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDelete(mutate self: cell, keyLen: int, key: int): bool
|
||||
fun iDictDelete(mutate self: cell?, keyLen: int, key: int): bool
|
||||
asm(key self keyLen) "DICTIDEL";
|
||||
|
||||
@pure
|
||||
fun uDictDelete(mutate self: cell, keyLen: int, key: int): bool
|
||||
fun uDictDelete(mutate self: cell?, keyLen: int, key: int): bool
|
||||
asm(key self keyLen) "DICTUDEL";
|
||||
|
||||
@pure
|
||||
fun sDictDelete(mutate self: cell, keyLen: int, key: slice): bool
|
||||
fun sDictDelete(mutate self: cell?, keyLen: int, key: slice): bool
|
||||
asm(key self keyLen) "DICTDEL";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, bool)
|
||||
fun iDictSetAndGet(mutate self: cell?, keyLen: int, key: int, value: slice): (slice?, bool)
|
||||
asm(value key self keyLen) "DICTISETGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictSetAndGet(mutate self: cell, keyLen: int, key: int, value: slice): (slice, bool)
|
||||
fun uDictSetAndGet(mutate self: cell?, keyLen: int, key: int, value: slice): (slice?, bool)
|
||||
asm(value key self keyLen) "DICTUSETGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictSetAndGet(mutate self: cell, keyLen: int, key: slice, value: slice): (slice, bool)
|
||||
fun sDictSetAndGet(mutate self: cell?, keyLen: int, key: slice, value: slice): (slice?, bool)
|
||||
asm(value key self keyLen) "DICTSETGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||
fun iDictSetAndGetRefOrNull(mutate self: cell?, keyLen: int, key: int, value: cell): cell?
|
||||
asm(value key self keyLen) "DICTISETGETOPTREF";
|
||||
|
||||
@pure
|
||||
fun uDictSetAndGetRefOrNull(mutate self: cell, keyLen: int, key: int, value: cell): cell
|
||||
fun uDictSetAndGetRefOrNull(mutate self: cell?, keyLen: int, key: int, value: cell): cell?
|
||||
asm(value key self keyLen) "DICTUSETGETOPTREF";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, bool)
|
||||
fun iDictDeleteAndGet(mutate self: cell?, keyLen: int, key: int): (slice?, bool)
|
||||
asm(key self keyLen) "DICTIDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteAndGet(mutate self: cell, keyLen: int, key: int): (slice, bool)
|
||||
fun uDictDeleteAndGet(mutate self: cell?, keyLen: int, key: int): (slice?, bool)
|
||||
asm(key self keyLen) "DICTUDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteAndGet(mutate self: cell, keyLen: int, key: slice): (slice, bool)
|
||||
fun sDictDeleteAndGet(mutate self: cell?, keyLen: int, key: slice): (slice?, bool)
|
||||
asm(key self keyLen) "DICTDELGET" "NULLSWAPIFNOT";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||
fun iDictSetBuilder(mutate self: cell?, keyLen: int, key: int, value: builder): void
|
||||
asm(value key self keyLen) "DICTISETB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilder(mutate self: cell, keyLen: int, key: int, value: builder): void
|
||||
fun uDictSetBuilder(mutate self: cell?, keyLen: int, key: int, value: builder): void
|
||||
asm(value key self keyLen) "DICTUSETB";
|
||||
|
||||
@pure
|
||||
fun sDictSetBuilder(mutate self: cell, keyLen: int, key: slice, value: builder): void
|
||||
fun sDictSetBuilder(mutate self: cell?, keyLen: int, key: slice, value: builder): void
|
||||
asm(value key self keyLen) "DICTSETB";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): bool
|
||||
fun iDictSetBuilderIfNotExists(mutate self: cell?, keyLen: int, key: int, value: builder): bool
|
||||
asm(value key self keyLen) "DICTIADDB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilderIfNotExists(mutate self: cell, keyLen: int, key: int, value: builder): bool
|
||||
fun uDictSetBuilderIfNotExists(mutate self: cell?, keyLen: int, key: int, value: builder): bool
|
||||
asm(value key self keyLen) "DICTUADDB";
|
||||
|
||||
@pure
|
||||
fun iDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): bool
|
||||
fun iDictSetBuilderIfExists(mutate self: cell?, keyLen: int, key: int, value: builder): bool
|
||||
asm(value key self keyLen) "DICTIREPLACEB";
|
||||
|
||||
@pure
|
||||
fun uDictSetBuilderIfExists(mutate self: cell, keyLen: int, key: int, value: builder): bool
|
||||
fun uDictSetBuilderIfExists(mutate self: cell?, keyLen: int, key: int, value: builder): bool
|
||||
asm(value key self keyLen) "DICTUREPLACEB";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, bool)
|
||||
fun iDictDeleteFirstAndGet(mutate self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (int, slice, bool)
|
||||
fun uDictDeleteFirstAndGet(mutate self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteFirstAndGet(mutate self: cell, keyLen: int): (slice, slice, bool)
|
||||
fun sDictDeleteFirstAndGet(mutate self: cell?, keyLen: int): (slice?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, bool)
|
||||
fun iDictDeleteLastAndGet(mutate self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictDeleteLastAndGet(mutate self: cell, keyLen: int): (int, slice, bool)
|
||||
fun uDictDeleteLastAndGet(mutate self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictDeleteLastAndGet(mutate self: cell, keyLen: int): (slice, slice, bool)
|
||||
fun sDictDeleteLastAndGet(mutate self: cell?, keyLen: int): (slice?, slice?, bool)
|
||||
asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetFirst(self: cell, keyLen: int): (int, slice, bool)
|
||||
fun iDictGetFirst(self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetFirst(self: cell, keyLen: int): (int, slice, bool)
|
||||
fun uDictGetFirst(self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetFirst(self: cell, keyLen: int): (slice, slice, bool)
|
||||
fun sDictGetFirst(self: cell?, keyLen: int): (slice?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, bool)
|
||||
fun iDictGetFirstAsRef(self: cell?, keyLen: int): (int?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetFirstAsRef(self: cell, keyLen: int): (int, cell, bool)
|
||||
fun uDictGetFirstAsRef(self: cell?, keyLen: int): (int?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetFirstAsRef(self: cell, keyLen: int): (slice, cell, bool)
|
||||
fun sDictGetFirstAsRef(self: cell?, keyLen: int): (slice?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetLast(self: cell, keyLen: int): (int, slice, bool)
|
||||
fun iDictGetLast(self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetLast(self: cell, keyLen: int): (int, slice, bool)
|
||||
fun uDictGetLast(self: cell?, keyLen: int): (int?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetLast(self: cell, keyLen: int): (slice, slice, bool)
|
||||
fun sDictGetLast(self: cell?, keyLen: int): (slice?, slice?, bool)
|
||||
asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetLastAsRef(self: cell, keyLen: int): (int, cell, bool)
|
||||
fun iDictGetLastAsRef(self: cell?, keyLen: int): (int?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetLastAsRef(self: cell, keyLen: int): (int, cell, bool)
|
||||
fun uDictGetLastAsRef(self: cell?, keyLen: int): (int?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun sDictGetLastAsRef(self: cell, keyLen: int): (slice, cell, bool)
|
||||
fun sDictGetLastAsRef(self: cell?, keyLen: int): (slice?, cell?, bool)
|
||||
asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun iDictGetNext(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetNext(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun uDictGetNext(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun iDictGetNextOrEqual(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetNextOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun uDictGetNextOrEqual(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
@pure
|
||||
fun iDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun iDictGetPrev(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetPrev(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun uDictGetPrev(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun iDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun iDictGetPrevOrEqual(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
||||
fun uDictGetPrevOrEqual(self: cell?, keyLen: int, pivot: int): (int?, slice?, bool)
|
||||
asm(pivot self keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2";
|
||||
|
||||
|
||||
|
@ -299,13 +300,13 @@ fun uDictGetPrevOrEqual(self: cell, keyLen: int, pivot: int): (int, slice, bool)
|
|||
*/
|
||||
|
||||
@pure
|
||||
fun prefixDictGet(self: cell, keyLen: int, key: slice): (slice, slice, slice, bool)
|
||||
fun prefixDictGet(self: cell?, keyLen: int, key: slice): (slice, slice?, slice?, bool)
|
||||
asm(key self keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||
|
||||
@pure
|
||||
fun prefixDictSet(mutate self: cell, keyLen: int, key: slice, value: slice): bool
|
||||
fun prefixDictSet(mutate self: cell?, keyLen: int, key: slice, value: slice): bool
|
||||
asm(value key self keyLen) "PFXDICTSET";
|
||||
|
||||
@pure
|
||||
fun prefixDictDelete(mutate self: cell, keyLen: int, key: slice): bool
|
||||
fun prefixDictDelete(mutate self: cell?, keyLen: int, key: slice): bool
|
||||
asm(key self keyLen) "PFXDICTDEL";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// A part of standard library for Tolk
|
||||
tolk 0.8
|
||||
tolk 0.9
|
||||
|
||||
/// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls.
|
||||
/// The primitive returns the current value of `c3`.
|
||||
|
|
|
@ -196,6 +196,13 @@ bool TLB::print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent, int re
|
|||
return pp.fail_unless(print_ref(pp, std::move(cell_ref)));
|
||||
}
|
||||
|
||||
bool TLB::print_ref(td::StringBuilder& sb, Ref<vm::Cell> cell_ref, int indent, int rec_limit) const {
|
||||
std::ostringstream ss;
|
||||
auto result = print_ref(ss, std::move(cell_ref), indent, rec_limit);
|
||||
sb << ss.str();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string TLB::as_string_skip(vm::CellSlice& cs, int indent) const {
|
||||
std::ostringstream os;
|
||||
print_skip(os, cs, indent);
|
||||
|
|
|
@ -246,7 +246,14 @@ class TLB {
|
|||
bool print(std::ostream& os, Ref<vm::CellSlice> cs_ref, int indent = 0, int rec_limit = 0) const {
|
||||
return print(os, *cs_ref, indent, rec_limit);
|
||||
}
|
||||
bool print(td::StringBuilder& sb, Ref<vm::CellSlice> cs_ref, int indent = 0, int rec_limit = 0) const {
|
||||
std::ostringstream ss;
|
||||
auto result = print(ss, *cs_ref, indent, rec_limit);
|
||||
sb << ss.str();
|
||||
return result;
|
||||
}
|
||||
bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0, int rec_limit = 0) const;
|
||||
bool print_ref(td::StringBuilder& sb, Ref<vm::Cell> cell_ref, int indent = 0, int rec_limit = 0) const;
|
||||
bool print_ref(int rec_limit, std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0) const {
|
||||
return print_ref(os, std::move(cell_ref), indent, rec_limit);
|
||||
}
|
||||
|
|
|
@ -1153,8 +1153,12 @@ td::Result<CellStorageStat::CellInfo> CellStorageStat::add_used_storage(Ref<vm::
|
|||
return ins.first->second;
|
||||
}
|
||||
}
|
||||
vm::CellSlice cs{vm::NoVm{}, std::move(cell)};
|
||||
return add_used_storage(std::move(cs), kill_dup, skip_count_root);
|
||||
vm::CellSlice cs{vm::NoVm{}, cell};
|
||||
TRY_RESULT(res, add_used_storage(std::move(cs), kill_dup, skip_count_root));
|
||||
if (kill_dup) {
|
||||
seen[cell->get_hash()] = res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NewCellStorageStat::add_cell(Ref<Cell> cell) {
|
||||
|
|
|
@ -101,9 +101,9 @@ class NewCellStorageStat {
|
|||
|
||||
private:
|
||||
const CellUsageTree* usage_tree_;
|
||||
std::set<vm::Cell::Hash> seen_;
|
||||
td::HashSet<vm::Cell::Hash> seen_;
|
||||
Stat stat_;
|
||||
std::set<vm::Cell::Hash> proof_seen_;
|
||||
td::HashSet<vm::Cell::Hash> proof_seen_;
|
||||
Stat proof_stat_;
|
||||
const NewCellStorageStat* parent_{nullptr};
|
||||
|
||||
|
@ -117,7 +117,7 @@ struct CellStorageStat {
|
|||
struct CellInfo {
|
||||
td::uint32 max_merkle_depth = 0;
|
||||
};
|
||||
std::map<vm::Cell::Hash, CellInfo> seen;
|
||||
td::HashMap<vm::Cell::Hash, CellInfo> seen;
|
||||
CellStorageStat() : cells(0), bits(0), public_cells(0) {
|
||||
}
|
||||
explicit CellStorageStat(unsigned long long limit_cells)
|
||||
|
@ -173,7 +173,7 @@ class ProofStorageStat {
|
|||
enum CellStatus {
|
||||
c_none = 0, c_prunned = 1, c_loaded = 2
|
||||
};
|
||||
std::map<vm::Cell::Hash, CellStatus> cells_;
|
||||
td::HashMap<vm::Cell::Hash, CellStatus> cells_;
|
||||
td::uint64 proof_size_ = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -1026,6 +1026,13 @@ bool CellSlice::print_rec(std::ostream& os, int indent) const {
|
|||
return print_rec(os, &limit, indent);
|
||||
}
|
||||
|
||||
bool CellSlice::print_rec(td::StringBuilder& sb, int indent) const {
|
||||
std::ostringstream ss;
|
||||
auto result = print_rec(ss, indent);
|
||||
sb << ss.str();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CellSlice::print_rec(int limit, std::ostream& os, int indent) const {
|
||||
return print_rec(os, &limit, indent);
|
||||
}
|
||||
|
|
|
@ -257,6 +257,7 @@ class CellSlice : public td::CntObject {
|
|||
void dump(std::ostream& os, int level = 0, bool endl = true) const;
|
||||
void dump_hex(std::ostream& os, int mode = 0, bool endl = false) const;
|
||||
bool print_rec(std::ostream& os, int indent = 0) const;
|
||||
bool print_rec(td::StringBuilder& sb, int indent = 0) const;
|
||||
bool print_rec(std::ostream& os, int* limit, int indent = 0) const;
|
||||
bool print_rec(int limit, std::ostream& os, int indent = 0) const;
|
||||
void error() const {
|
||||
|
|
|
@ -1761,6 +1761,10 @@ int exec_send_message(VmState* st) {
|
|||
vm::VmStorageStat stat(max_cells);
|
||||
CellSlice cs = load_cell_slice(msg_cell);
|
||||
cs.skip_first(cs.size());
|
||||
if (st->get_global_version() >= 10 && have_extra_currencies) {
|
||||
// Skip extra currency dict
|
||||
cs.advance_refs(1);
|
||||
}
|
||||
stat.add_storage(cs);
|
||||
|
||||
if (!ext_msg) {
|
||||
|
@ -1773,7 +1777,9 @@ int exec_send_message(VmState* st) {
|
|||
if (value.is_null()) {
|
||||
throw VmError{Excno::type_chk, "invalid param BALANCE"};
|
||||
}
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
if (st->get_global_version() < 10) {
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
}
|
||||
} else if (mode & 64) { // value += value of incoming message
|
||||
Ref<Tuple> balance = get_param(st, 11).as_tuple();
|
||||
if (balance.is_null()) {
|
||||
|
@ -1784,7 +1790,9 @@ int exec_send_message(VmState* st) {
|
|||
throw VmError{Excno::type_chk, "invalid param INCOMINGVALUE"};
|
||||
}
|
||||
value += balance_grams;
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
if (st->get_global_version() < 10) {
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,4 +134,25 @@ Example: if the last masterchain block seqno is `19071` then the list contains b
|
|||
- `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT`
|
||||
- Now setting the contract code to a library cell does not consume additional gas on execution of the code.
|
||||
- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts).
|
||||
- Fix recursive jump to continuations with non-null control data.
|
||||
- Fix recursive jump to continuations with non-null control data.
|
||||
|
||||
## Version 10
|
||||
|
||||
### Extra currencies
|
||||
- Internal messages cannot carry more than 2 different extra currencies. The limit can be changed in size limits config (`ConfigParam 43`).
|
||||
- Amount of an extra currency in an output action "send message" can be zero.
|
||||
- In action phase zero values are automatically deleted from the dictionary before sending.
|
||||
- However, the size of the extra currency dictionary in the "send message" action should not be greater than 2 (or the value in size limits config).
|
||||
- Extra currency dictionary is not counted in message size and does not affect message fees.
|
||||
- Message mode `+64` (carry all remaining message balance) is now considered as "carry all remaining TONs from message balance".
|
||||
- Message mode `+128` (carry all remaining account balance) is now considered as "carry all remaining TONs from account balance".
|
||||
- Message mode `+32` (delete account if balance is zero) deletes account if it has zero TONs, regardless of extra currencies.
|
||||
- Deleted accounts with extra currencies become `account_uninit`, extra currencies remain on the account.
|
||||
- `SENDMSG` in TVM calculates message size and fees without extra currencies, uses new `+64` and `+128` mode behavior.
|
||||
- `SENDMSG` does not check the number of extra currencies.
|
||||
- Extra currency dictionary is not counted in the account size and does not affect storage fees.
|
||||
- Accounts with already existing extra currencies will get their sizes recomputed without EC only after modifying `AccountState`.
|
||||
|
||||
### TVM changes
|
||||
- `SENDMSG` calculates messages size and fees without extra currencies, uses new +64 and +128 mode behavior.
|
||||
- `SENDMSG` does not check the number of extra currencies.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif()
|
||||
option(EMULATOR_STATIC "Build emulator as static library" OFF)
|
||||
|
||||
set(EMULATOR_STATIC_SOURCE
|
||||
transaction-emulator.cpp
|
||||
|
@ -22,7 +20,7 @@ include(GenerateExportHeader)
|
|||
add_library(emulator_static STATIC ${EMULATOR_STATIC_SOURCE})
|
||||
target_link_libraries(emulator_static PUBLIC ton_crypto smc-envelope)
|
||||
|
||||
if (USE_EMSCRIPTEN)
|
||||
if (EMULATOR_STATIC OR USE_EMSCRIPTEN)
|
||||
add_library(emulator STATIC ${EMULATOR_SOURCE})
|
||||
else()
|
||||
add_library(emulator SHARED ${EMULATOR_SOURCE})
|
||||
|
@ -35,7 +33,7 @@ else()
|
|||
endif()
|
||||
|
||||
generate_export_header(emulator EXPORT_FILE_NAME ${CMAKE_CURRENT_BINARY_DIR}/emulator_export.h)
|
||||
if (USE_EMSCRIPTEN)
|
||||
if (EMULATOR_STATIC OR USE_EMSCRIPTEN)
|
||||
target_compile_definitions(emulator PUBLIC EMULATOR_STATIC_DEFINE)
|
||||
endif()
|
||||
target_include_directories(emulator PUBLIC
|
||||
|
|
|
@ -16,6 +16,7 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
block::StoragePhaseConfig storage_phase_cfg{&storage_prices};
|
||||
block::ComputePhaseConfig compute_phase_cfg;
|
||||
block::ActionPhaseConfig action_phase_cfg;
|
||||
block::SerializeConfig serialize_config;
|
||||
td::RefInt256 masterchain_create_fee, basechain_create_fee;
|
||||
|
||||
if (!utime) {
|
||||
|
@ -25,11 +26,9 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
utime = (unsigned)std::time(nullptr);
|
||||
}
|
||||
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(*config_, prev_blocks_info_, &old_mparams,
|
||||
&storage_prices, &storage_phase_cfg,
|
||||
&rand_seed_, &compute_phase_cfg,
|
||||
&action_phase_cfg, &masterchain_create_fee,
|
||||
&basechain_create_fee, account.workchain, utime);
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(
|
||||
*config_, prev_blocks_info_, &old_mparams, &storage_prices, &storage_phase_cfg, &rand_seed_, &compute_phase_cfg,
|
||||
&action_phase_cfg, &serialize_config, &masterchain_create_fee, &basechain_create_fee, account.workchain, utime);
|
||||
if(fetch_res.is_error()) {
|
||||
return fetch_res.move_as_error_prefix("cannot fetch config params ");
|
||||
}
|
||||
|
@ -66,7 +65,7 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
return std::make_unique<TransactionEmulator::EmulationExternalNotAccepted>(std::move(vm_log), vm_exit_code, elapsed);
|
||||
}
|
||||
|
||||
if (!trans->serialize()) {
|
||||
if (!trans->serialize(serialize_config)) {
|
||||
return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex());
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ void OverlayOutboundFecBroadcast::alarm() {
|
|||
fec_type_.size(), flags_, std::move(X.data), X.id, fec_type_, date_);
|
||||
}
|
||||
|
||||
alarm_timestamp() = td::Timestamp::in(0.010);
|
||||
alarm_timestamp() = td::Timestamp::in(delay_);
|
||||
|
||||
if (seqno_ >= to_send_) {
|
||||
stop();
|
||||
|
@ -46,8 +46,9 @@ void OverlayOutboundFecBroadcast::start_up() {
|
|||
|
||||
OverlayOutboundFecBroadcast::OverlayOutboundFecBroadcast(td::BufferSlice data, td::uint32 flags,
|
||||
td::actor::ActorId<OverlayImpl> overlay,
|
||||
PublicKeyHash local_id)
|
||||
PublicKeyHash local_id, double speed_multiplier)
|
||||
: flags_(flags) {
|
||||
delay_ /= speed_multiplier;
|
||||
CHECK(data.size() <= (1 << 27));
|
||||
local_id_ = local_id;
|
||||
overlay_ = std::move(overlay);
|
||||
|
@ -63,9 +64,10 @@ OverlayOutboundFecBroadcast::OverlayOutboundFecBroadcast(td::BufferSlice data, t
|
|||
}
|
||||
|
||||
td::actor::ActorId<OverlayOutboundFecBroadcast> OverlayOutboundFecBroadcast::create(
|
||||
td::BufferSlice data, td::uint32 flags, td::actor::ActorId<OverlayImpl> overlay, PublicKeyHash local_id) {
|
||||
return td::actor::create_actor<OverlayOutboundFecBroadcast>(td::actor::ActorOptions().with_name("bcast"),
|
||||
std::move(data), flags, overlay, local_id)
|
||||
td::BufferSlice data, td::uint32 flags, td::actor::ActorId<OverlayImpl> overlay, PublicKeyHash local_id,
|
||||
double speed_multiplier) {
|
||||
return td::actor::create_actor<OverlayOutboundFecBroadcast>(
|
||||
td::actor::ActorOptions().with_name("bcast"), std::move(data), flags, overlay, local_id, speed_multiplier)
|
||||
.release();
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class OverlayOutboundFecBroadcast : public td::actor::Actor {
|
|||
PublicKeyHash local_id_;
|
||||
Overlay::BroadcastDataHash data_hash_;
|
||||
td::uint32 flags_ = 0;
|
||||
double delay_ = 0.010;
|
||||
td::int32 date_;
|
||||
std::unique_ptr<td::fec::Encoder> encoder_;
|
||||
td::actor::ActorId<OverlayImpl> overlay_;
|
||||
|
@ -45,9 +46,9 @@ class OverlayOutboundFecBroadcast : public td::actor::Actor {
|
|||
public:
|
||||
static td::actor::ActorId<OverlayOutboundFecBroadcast> create(td::BufferSlice data, td::uint32 flags,
|
||||
td::actor::ActorId<OverlayImpl> overlay,
|
||||
PublicKeyHash local_id);
|
||||
PublicKeyHash local_id, double speed_multiplier = 1.0);
|
||||
OverlayOutboundFecBroadcast(td::BufferSlice data, td::uint32 flags, td::actor::ActorId<OverlayImpl> overlay,
|
||||
PublicKeyHash local_id);
|
||||
PublicKeyHash local_id, double speed_multiplier = 1.0);
|
||||
|
||||
void alarm() override;
|
||||
void start_up() override;
|
||||
|
|
|
@ -63,7 +63,7 @@ td::actor::ActorOwn<Overlay> Overlay::create_private(
|
|||
return td::actor::create_actor<OverlayImpl>(
|
||||
overlay_actor_name(overlay_id), keyring, adnl, manager, dht_node, local_id, std::move(overlay_id),
|
||||
OverlayType::FixedMemberList, std::move(nodes), std::vector<PublicKeyHash>(), OverlayMemberCertificate{},
|
||||
std::move(callback), std::move(rules), std::move(scope));
|
||||
std::move(callback), std::move(rules), std::move(scope), std::move(opts));
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<Overlay> Overlay::create_semiprivate(
|
||||
|
@ -99,6 +99,7 @@ OverlayImpl::OverlayImpl(td::actor::ActorId<keyring::Keyring> keyring, td::actor
|
|||
overlay_id_ = id_full_.compute_short_id();
|
||||
frequent_dht_lookup_ = opts_.frequent_dht_lookup_;
|
||||
peer_list_.local_member_flags_ = opts_.local_overlay_member_flags_;
|
||||
opts_.broadcast_speed_multiplier_ = std::max(opts_.broadcast_speed_multiplier_, 1e-9);
|
||||
|
||||
VLOG(OVERLAY_INFO) << this << ": creating";
|
||||
|
||||
|
@ -490,7 +491,8 @@ void OverlayImpl::send_broadcast_fec(PublicKeyHash send_as, td::uint32 flags, td
|
|||
VLOG(OVERLAY_WARNING) << "broadcast source certificate is invalid";
|
||||
return;
|
||||
}
|
||||
OverlayOutboundFecBroadcast::create(std::move(data), flags, actor_id(this), send_as);
|
||||
OverlayOutboundFecBroadcast::create(std::move(data), flags, actor_id(this), send_as,
|
||||
opts_.broadcast_speed_multiplier_);
|
||||
}
|
||||
|
||||
void OverlayImpl::print(td::StringBuilder &sb) {
|
||||
|
|
|
@ -269,6 +269,7 @@ struct OverlayOptions {
|
|||
td::uint32 nodes_to_send_ = 4;
|
||||
td::uint32 propagate_broadcast_to_ = 5;
|
||||
td::uint32 default_permanent_members_flags_ = 0;
|
||||
double broadcast_speed_multiplier_ = 1.0;
|
||||
};
|
||||
|
||||
class Overlays : public td::actor::Actor {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
## 2025.02 Update
|
||||
1. Series of improvement/fixes for `Config8.version >= 9`, check [GlobalVersions.md](./doc/GlobalVersions.md)
|
||||
2. Fix for better discovery of updated nodes' (validators') IPs: retry dht queries
|
||||
3. Series of improvements for extra currency adoption: fixed c7 in rungetmethod, reserve modes
|
||||
4. TVM: Fix processing continuation control data on deep jump
|
||||
5. A few fixes of tl-b schemes: crc computation, incorrect tag for merkle proofs, advance_ext, NatWidth print
|
||||
6. Emulator improvements: fix setting libraries, extracurrency support
|
||||
7. Increase of gas limit for unlocking highload-v2 wallets locked in the beginning of 2024
|
||||
8. Validator console improvement: dashed names, better shard formats
|
||||
## 2025.03 Update
|
||||
1. New extracurrency behavior introduced, check [GlobalVersions.md](./doc/GlobalVersions.md#version-10)
|
||||
2. Optmization of validation process, in particular CellStorageStat.
|
||||
3. Flag for speeding up broadcasts in various overlays.
|
||||
4. Fixes for static builds for emulator and tonlibjson
|
||||
5. Improving getstats output: add
|
||||
* Liteserver queries count
|
||||
* Collated/validated blocks count, number of active sessions
|
||||
* Persistent state sizes
|
||||
* Initial sync progress
|
||||
6. Fixes in logging, TON Storage, external message checking, persistent state downloading, UB in tonlib
|
||||
|
||||
|
||||
Besides the work of the core team, this update is based on the efforts of @dbaranovstonfi from StonFi(libraries in emulator), @Rexagon (ret on deep jumps), @tvorogme from DTon (`advance_ext`), Nan from Zellic (`stk_und` and JNI)
|
||||
Besides the work of the core team, this update is based on the efforts of @Sild from StonFi(UB in tonlib).
|
||||
|
|
|
@ -251,7 +251,7 @@ void PeerActor::loop_update_init() {
|
|||
}
|
||||
s = s.substr(peer_init_offset_, UPDATE_INIT_BLOCK_SIZE);
|
||||
auto query = create_update_query(ton::create_tl_object<ton::ton_api::storage_updateInit>(
|
||||
td::BufferSlice(s), (int)peer_init_offset_, to_ton_api(node_state)));
|
||||
td::BufferSlice(s), (int)peer_init_offset_ * 8, to_ton_api(node_state)));
|
||||
|
||||
// take care about update_state_query initial state
|
||||
update_state_query_.state = node_state;
|
||||
|
@ -502,11 +502,11 @@ void PeerActor::process_update_peer_parts(const tl_object_ptr<ton_api::storage_U
|
|||
},
|
||||
[&](const ton::ton_api::storage_updateState &state) {},
|
||||
[&](const ton::ton_api::storage_updateInit &init) {
|
||||
LOG(DEBUG) << "Processing updateInit query (offset=" << init.have_pieces_offset_ * 8 << ")";
|
||||
LOG(DEBUG) << "Processing updateInit query (offset=" << init.have_pieces_offset_ << ")";
|
||||
td::Bitset new_bitset;
|
||||
new_bitset.set_raw(init.have_pieces_.as_slice().str());
|
||||
size_t offset = init.have_pieces_offset_ * 8;
|
||||
for (auto size = new_bitset.size(), i = size_t(0); i < size; i++) {
|
||||
size_t offset = init.have_pieces_offset_;
|
||||
for (auto size = new_bitset.size(), i = (size_t)0; i < size; i++) {
|
||||
if (new_bitset.get(i)) {
|
||||
add_piece(static_cast<PartId>(offset + i));
|
||||
}
|
||||
|
|
|
@ -128,6 +128,10 @@ inline Timestamp &operator+=(Timestamp &a, double b) {
|
|||
return a;
|
||||
}
|
||||
|
||||
inline double operator-(const Timestamp &a, const Timestamp &b) {
|
||||
return a.at() - b.at();
|
||||
}
|
||||
|
||||
template <class StorerT>
|
||||
void store(const Timestamp ×tamp, StorerT &storer) {
|
||||
storer.store_binary(timestamp.at() - Time::now() + Clocks::system());
|
||||
|
|
|
@ -264,8 +264,8 @@ class Logger {
|
|||
sb_ << other;
|
||||
return *this;
|
||||
}
|
||||
LambdaPrintHelper<td::Logger> operator<<(const LambdaPrint &) {
|
||||
return LambdaPrintHelper<td::Logger>{*this};
|
||||
LambdaPrintHelper<td::StringBuilder> operator<<(const LambdaPrint &) {
|
||||
return LambdaPrintHelper<td::StringBuilder>{sb_};
|
||||
}
|
||||
|
||||
MutableCSlice as_cslice() {
|
||||
|
|
|
@ -2,7 +2,7 @@ import "@stdlib/tvm-lowlevel"
|
|||
|
||||
fun pair_first<X, Y>(p: [X, Y]): X asm "FIRST";
|
||||
|
||||
fun one(dummy: tuple) {
|
||||
fun one(dummy: tuple?) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -144,15 +144,16 @@ fun test95() {
|
|||
"""
|
||||
test95 PROC:<{
|
||||
...
|
||||
next GETGLOB // '10
|
||||
3 PUSHINT // '10 '12=3
|
||||
4 PUSHINT // '10 '12=3 '13=4
|
||||
5 PUSHINT // '10 '12=3 '13=4 '14=5
|
||||
TRIPLE // '15 '16
|
||||
next SETGLOB
|
||||
next GETGLOB // g_next
|
||||
3 PUSHINT // g_next '14=3
|
||||
4 PUSHINT // g_next '14=3 '15=4
|
||||
5 PUSHINT // g_next '14=3 '15=4 '16=5
|
||||
TRIPLE // '10 '11
|
||||
SWAP
|
||||
cur SETGLOB
|
||||
cur GETGLOB // '17
|
||||
next GETGLOB // '17 '18
|
||||
next SETGLOB
|
||||
cur GETGLOB // g_cur
|
||||
next GETGLOB // g_cur g_next
|
||||
}>
|
||||
"""
|
||||
*/
|
||||
|
|
|
@ -147,5 +147,5 @@ fun main() {
|
|||
// x.0 x.1
|
||||
"""
|
||||
|
||||
@code_hash 7627024945492125068389905298530400936797031708759561372406088054030801992712
|
||||
@code_hash 61280273714870328160131559159866470128402169974050439159015534193532598351244
|
||||
*/
|
||||
|
|
|
@ -26,10 +26,189 @@ fun typesAsIdentifiers(builder: builder) {
|
|||
return int;
|
||||
}
|
||||
|
||||
global callOrder: tuple;
|
||||
|
||||
fun getTensor_12() {
|
||||
callOrder.tuplePush(100);
|
||||
return (1, 2);
|
||||
}
|
||||
fun getTensor_1X(x: int) {
|
||||
callOrder.tuplePush(101);
|
||||
return (1, x);
|
||||
}
|
||||
fun getTuple_12() {
|
||||
callOrder.tuplePush(110);
|
||||
return [1, 2];
|
||||
}
|
||||
fun getTuple_1X(x: int) {
|
||||
callOrder.tuplePush(111);
|
||||
return [1, x];
|
||||
}
|
||||
fun getUntypedTuple_12() {
|
||||
callOrder.tuplePush(120);
|
||||
var t = createEmptyTuple(); t.tuplePush(1); t.tuplePush(2);
|
||||
return t;
|
||||
}
|
||||
fun getUntypedTuple_1X(x: int) {
|
||||
callOrder.tuplePush(121);
|
||||
var t = createEmptyTuple(); t.tuplePush(1); t.tuplePush(x);
|
||||
return t;
|
||||
}
|
||||
fun getIntValue5() {
|
||||
callOrder.tuplePush(10);
|
||||
return 5;
|
||||
}
|
||||
fun getIntValueX(x: int) {
|
||||
callOrder.tuplePush(11);
|
||||
return x;
|
||||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test102() {
|
||||
callOrder = createEmptyTuple();
|
||||
var x = 0;
|
||||
getTensor_12().0 = getIntValue5();
|
||||
getTensor_1X(5).1 = getIntValue5();
|
||||
getTensor_1X(x = 10).0 = getIntValueX(x);
|
||||
return (callOrder, x);
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103() {
|
||||
callOrder = createEmptyTuple();
|
||||
var x = 0;
|
||||
getTuple_12().0 = getIntValue5();
|
||||
getTuple_1X(5).1 = getIntValue5();
|
||||
getTuple_1X(x = 10).0 = getIntValueX(x);
|
||||
return (callOrder, x);
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test104() {
|
||||
callOrder = createEmptyTuple();
|
||||
var x = 0;
|
||||
getUntypedTuple_12().0 = getIntValue5();
|
||||
getUntypedTuple_1X(5).1 = getIntValue5();
|
||||
getUntypedTuple_1X(x = 10).0 = getIntValueX(x);
|
||||
return (callOrder, x);
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
callOrder = createEmptyTuple();
|
||||
getTensor_12().0 = getTensor_1X(getIntValue5()).1 = getIntValueX(getTensor_12().1);
|
||||
return callOrder;
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
callOrder = createEmptyTuple();
|
||||
getTuple_12().0 = getTuple_1X(getIntValue5()).1 = getIntValueX(getTuple_12().1);
|
||||
return callOrder;
|
||||
}
|
||||
|
||||
global t107: (int, int);
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
((t107 = (1, 2)).0, (t107 = (3, 4)).1) = (5, 6);
|
||||
return t107;
|
||||
}
|
||||
|
||||
global g108: int;
|
||||
fun assertEq(a: int, b: int) {
|
||||
assert(a == b, 10);
|
||||
return b;
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108() {
|
||||
callOrder = createEmptyTuple();
|
||||
g108 = 0;
|
||||
getTensor_1X(g108 = 8).1 = assertEq(g108, 8);
|
||||
return (callOrder, g108);
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun test109() {
|
||||
callOrder = createEmptyTuple();
|
||||
var x = 0;
|
||||
[getTuple_12().0, getTuple_1X(x = getIntValue5()).1, getTuple_1X(x += 10).0] = [getIntValue5(), getIntValue5(), getIntValueX(x)];
|
||||
return (callOrder, x);
|
||||
}
|
||||
|
||||
global g110: int;
|
||||
global t110: (int, int);
|
||||
|
||||
@method_id(110)
|
||||
fun test110() {
|
||||
callOrder = createEmptyTuple();
|
||||
var xy = [0, 0];
|
||||
[xy.0, getTuple_1X(g110 = 8).0] = [g110 += 5, getIntValueX(g110 += 10)];
|
||||
[xy.1, getTuple_1X((t110 = (8, 9)).0).1] = [t110.0 += 5, getIntValueX(t110.1 += 10)];
|
||||
return (xy, callOrder, g110, t110);
|
||||
}
|
||||
|
||||
@method_id(111)
|
||||
fun test111() {
|
||||
callOrder = createEmptyTuple();
|
||||
var z = -1;
|
||||
var xy = [0, z = 0];
|
||||
var rhs = [getIntValueX(xy.1 += 10), xy.1, xy.0, z += 50];
|
||||
[xy.0, getTuple_1X(g110 = 8 + getIntValueX(xy.1)).0, xy.1, z] = rhs;
|
||||
return (xy, g110, callOrder, z);
|
||||
}
|
||||
|
||||
@method_id(112)
|
||||
fun test112() {
|
||||
var xy = [1, 2];
|
||||
((((xy))).0, ((xy.1))) = ((xy).1, ((xy.0)));
|
||||
return xy;
|
||||
}
|
||||
|
||||
@method_id(113)
|
||||
fun test113() {
|
||||
var (a, t, z) = (1, [2,3], (-1,-1));
|
||||
(a, t, a, z, t.1, z.1) = (10, [a,12], 13, (a, t.1), 14, t.1);
|
||||
return (a, t, z);
|
||||
}
|
||||
|
||||
global g114: int;
|
||||
global t114: [int, int];
|
||||
global z114: (int, int);
|
||||
|
||||
@method_id(114)
|
||||
fun test114() {
|
||||
g114 = 1;
|
||||
t114 = [2, 3];
|
||||
(g114, t114, g114, z114, t114.1, z114.1) = (10, [g114,12], 13, (g114, t114.1), 14, t114.1);
|
||||
return (g114, t114, z114);
|
||||
}
|
||||
|
||||
@method_id(115)
|
||||
fun test115() {
|
||||
callOrder = createEmptyTuple();
|
||||
var x = 0;
|
||||
var y = 0;
|
||||
[getTensor_1X(x = 5).0, y] = getTuple_1X(x = 9);
|
||||
return (callOrder, x, y);
|
||||
}
|
||||
|
||||
@method_id(116)
|
||||
fun test116() {
|
||||
var (a,b,c,d) = (0,0,0,0);
|
||||
var rhs = [1, 2, 3, 4];
|
||||
var rhs2 = ([a,b,c,d] = rhs);
|
||||
__expect_type(rhs2, "[int, int, int, int]");
|
||||
return (a, b, c, d, rhs2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun main(value: int) {
|
||||
var (x: int, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
|
||||
var (x: int?, y) = (autoInferIntNull(value), autoInferIntNull(value * 2));
|
||||
if (x == null && y == null) { return null; }
|
||||
return x == null || y == null ? -1 : x + y;
|
||||
return x == null || y == null ? -1 : x! + y!;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,4 +216,35 @@ fun main(value: int) {
|
|||
@testcase | 0 | 6 | -1
|
||||
@testcase | 0 | 11 | (null)
|
||||
@testcase | 101 | 78 | 88
|
||||
@testcase | 102 | | [ 100 10 101 10 101 11 ] 10
|
||||
@testcase | 103 | | [ 110 10 111 10 111 11 ] 10
|
||||
@testcase | 104 | | [ 120 10 121 10 121 11 ] 10
|
||||
@testcase | 105 | | [ 100 10 101 100 11 ]
|
||||
@testcase | 106 | | [ 110 10 111 110 11 ]
|
||||
@testcase | 107 | | 3 4
|
||||
@testcase | 108 | | [ 101 ] 8
|
||||
@testcase | 109 | | [ 110 10 111 111 10 10 11 ] 15
|
||||
@testcase | 110 | | [ 13 13 ] [ 111 11 111 11 ] 23 13 19
|
||||
@testcase | 111 | | [ 10 0 ] 18 [ 11 11 111 ] 50
|
||||
@testcase | 112 | | [ 2 1 ]
|
||||
@testcase | 113 | | 13 [ 1 14 ] 1 3
|
||||
@testcase | 114 | | 13 [ 1 14 ] 1 3
|
||||
@testcase | 115 | | [ 101 111 ] 9 9
|
||||
@testcase | 116 | | 1 2 3 4 [ 1 2 3 4 ]
|
||||
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test116 PROC:<{
|
||||
//
|
||||
1 PUSHINT // '10=1
|
||||
2 PUSHINT // '10=1 '11=2
|
||||
3 PUSHINT // '10=1 '11=2 '12=3
|
||||
4 PUSHINT // '10=1 '11=2 '12=3 '13=4
|
||||
4 TUPLE // rhs
|
||||
DUP // rhs rhs
|
||||
4 UNTUPLE // rhs2 a b c d
|
||||
4 ROLL // a b c d rhs2
|
||||
}>
|
||||
"""
|
||||
*/
|
||||
|
|
|
@ -8,7 +8,7 @@ fun unnamed_args(_: int, _: slice, _: int) {
|
|||
return true;
|
||||
}
|
||||
|
||||
fun main(x: int, y: int, z: int): bool {
|
||||
fun main(x: int, y: int, z: int): bool? {
|
||||
op = `_+_`;
|
||||
if (0) { return null; }
|
||||
return check_assoc(x, y, z);
|
||||
|
|
|
@ -32,7 +32,8 @@ fun test1(): [int,int,int,int,int] {
|
|||
fun test2(): [int,int,int] {
|
||||
var b: builder = beginCell().myStoreInt(1, 32);
|
||||
b = b.myStoreInt(2, 32);
|
||||
b.myStoreInt(3, 32);
|
||||
// operator ! here and below is used just for testing purposes, it doesn't affect the result
|
||||
b!.myStoreInt(3, 32);
|
||||
|
||||
var cs: slice = b.endCell().beginParse();
|
||||
var one: int = cs.myLoadInt(32);
|
||||
|
@ -43,14 +44,14 @@ fun test2(): [int,int,int] {
|
|||
|
||||
@method_id(103)
|
||||
fun test3(ret: int): int {
|
||||
val same: int = beginCell().storeUint(ret,32).endCell().beginParse().loadUint(32);
|
||||
val same: int = beginCell()!.storeUint(ret,32).endCell().beginParse().loadUint(32);
|
||||
return same;
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test4(): [int,int] {
|
||||
var b: builder = beginCell().myStoreInt(1, 32);
|
||||
b = b.storeInt(2, 32).storeInt(3, 32);
|
||||
var b: builder = (beginCell() as builder).myStoreInt(1, 32);
|
||||
b = b!.storeInt(2, 32)!.storeInt(3, 32);
|
||||
|
||||
var cs: slice = b.endCell().beginParse();
|
||||
var (one, _, three) = (cs.getFirstBits(32).loadUint(32), cs.skipBits(64), cs.load_u32());
|
||||
|
@ -116,7 +117,7 @@ fun test10() {
|
|||
fun test11() {
|
||||
var s: slice = beginCell().storeInt(1, 32).storeInt(2, 32).storeInt(3, 32).storeInt(4, 32).storeInt(5, 32).storeInt(6, 32).storeInt(7, 32).endCell().beginParse();
|
||||
var size1 = getRemainingBitsCount(s);
|
||||
s.skipBits(32);
|
||||
s!.skipBits(32);
|
||||
var s1: slice = s.getFirstBits(64);
|
||||
var n1 = s1.loadInt(32);
|
||||
var size2 = getRemainingBitsCount(s);
|
||||
|
|
|
@ -35,7 +35,7 @@ Below, I just give examples of @fif_codegen tag:
|
|||
"""
|
||||
main PROC:<{
|
||||
// s
|
||||
17 PUSHINT // s '1=17
|
||||
17 PUSHINT // s '3=17
|
||||
OVER // s z=17 t
|
||||
WHILE:<{
|
||||
...
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import "@stdlib/tvm-dicts"
|
||||
|
||||
fun addIntToIDict(mutate self: cell, key: int, number: int): void {
|
||||
fun addIntToIDict(mutate self: cell?, key: int, number: int): void {
|
||||
return self.iDictSetBuilder(32, key, beginCell().storeInt(number, 32));
|
||||
}
|
||||
|
||||
fun calculateDictLen(d: cell) {
|
||||
fun calculateDictLen(d: cell?) {
|
||||
var len = 0;
|
||||
var (k, v, f) = d.uDictGetFirst(32);
|
||||
while (f) {
|
||||
len += 1;
|
||||
(k, v, f) = d.uDictGetNext(32, k);
|
||||
(k, v, f) = d.uDictGetNext(32, k!);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
@ -25,13 +25,13 @@ fun loadTwoDigitNumberFromSlice(mutate self: slice): int {
|
|||
fun test101(getK1: int, getK2: int, getK3: int) {
|
||||
var dict = createEmptyDict();
|
||||
dict.uDictSetBuilder(32, 1, beginCell().storeUint(1, 32));
|
||||
var (old1: slice, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
|
||||
var (old2: slice, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
|
||||
var (cur3: slice, found3) = dict.uDictGet(32, getK3);
|
||||
var (old1: slice?, found1) = dict.uDictSetAndGet(32, getK1, beginCell().storeUint(2, 32).endCell().beginParse());
|
||||
var (old2: slice?, found2) = dict.uDictSetAndGet(32, getK2, beginCell().storeUint(3, 32).endCell().beginParse());
|
||||
var (cur3: slice?, found3) = dict.uDictGet(32, getK3);
|
||||
return (
|
||||
found1 ? old1.loadUint(32) : -1,
|
||||
found2 ? old2.loadUint(32) : -1,
|
||||
found3 ? cur3.loadUint(32) : -1
|
||||
found1 ? old1!.loadUint(32) : -1,
|
||||
found2 ? old2!.loadUint(32) : -1,
|
||||
found3 ? cur3!.loadUint(32) : -1
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ fun test102() {
|
|||
while (!shouldBreak) {
|
||||
var (kDel, kVal, wasDel) = dict.iDictDeleteLastAndGet(32);
|
||||
if (wasDel) {
|
||||
deleted.tuplePush([kDel, kVal.loadInt(32)]);
|
||||
deleted.tuplePush([kDel, kVal!.loadInt(32)]);
|
||||
} else {
|
||||
shouldBreak = true;
|
||||
}
|
||||
|
@ -82,14 +82,14 @@ fun test104() {
|
|||
var (old2, _) = dict.sDictDeleteAndGet(32, "key1");
|
||||
var (restK, restV, _) = dict.sDictGetFirst(32);
|
||||
var (restK1, restV1, _) = dict.sDictDeleteLastAndGet(32);
|
||||
assert (restK.isSliceBitsEqual(restK1)) throw 123;
|
||||
assert (restV.isSliceBitsEqual(restV1)) throw 123;
|
||||
assert (restK!.isSliceBitsEqual(restK1!)) throw 123;
|
||||
assert (restV!.isSliceBitsEqual(restV1!)) throw 123;
|
||||
return (
|
||||
old1.loadTwoDigitNumberFromSlice(),
|
||||
old2.loadTwoDigitNumberFromSlice(),
|
||||
restV.loadTwoDigitNumberFromSlice(),
|
||||
restK.loadTwoDigitNumberFromSlice(),
|
||||
restK.loadTwoDigitNumberFromSlice()
|
||||
old1!.loadTwoDigitNumberFromSlice(),
|
||||
old2!.loadTwoDigitNumberFromSlice(),
|
||||
restV!.loadTwoDigitNumberFromSlice(),
|
||||
restK!.loadTwoDigitNumberFromSlice(),
|
||||
restK!.loadTwoDigitNumberFromSlice()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -49,17 +49,17 @@ fun manyEq<T1, T2, T3>(a: T1, b: T2, c: T3): [T1, T2, T3] {
|
|||
fun test104(f: int) {
|
||||
var result = (
|
||||
manyEq(1 ? 1 : 1, f ? 0 : null, !f ? getTwo() as int : null),
|
||||
manyEq(f ? null as int : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool(), eq4(f))
|
||||
manyEq(f ? null as int? : eq2(2), beginCell().storeBool(true).endCell().beginParse().loadBool(), eq4(f))
|
||||
);
|
||||
__expect_type(result, "([int, int, int], [int, bool, int])");
|
||||
__expect_type(result, "([int, int?, int?], [int?, bool, int])");
|
||||
return result;
|
||||
}
|
||||
|
||||
fun calcSum<X>(x: X, y: X) { return x + y; }
|
||||
fun calcSum<X>(x: X, y: X) { return x! + y!; }
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
if (0) { calcSum(((0)), null); }
|
||||
if (0) { calcSum(((0 as int?)), null); }
|
||||
return (calcSum(1, 2));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import "@stdlib/tvm-dicts"
|
||||
|
||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell {
|
||||
var dict: cell = createEmptyDict();
|
||||
fun prepareDict_3_30_4_40_5_x(valueAt5: int): cell? {
|
||||
var dict: cell? = createEmptyDict();
|
||||
dict.iDictSetBuilder(32, 3, beginCell().storeInt(30, 32));
|
||||
dict.iDictSetBuilder(32, 4, beginCell().storeInt(40, 32));
|
||||
dict.iDictSetBuilder(32, 5, beginCell().storeInt(valueAt5, 32));
|
||||
return dict;
|
||||
}
|
||||
|
||||
fun lookupIdxByValue(idict32: cell, value: int): int {
|
||||
var cur_key = -1;
|
||||
fun lookupIdxByValue(idict32: cell?, value: int): int {
|
||||
var cur_key: int? = -1;
|
||||
do {
|
||||
var (cur_key redef, cs: slice, found: bool) = idict32.iDictGetNext(32, cur_key);
|
||||
var (cur_key redef, cs: slice?, found: bool) = idict32.iDictGetNext(32, cur_key!);
|
||||
// one-line condition (via &) doesn't work, since right side is calculated immediately
|
||||
if (found) {
|
||||
if (cs.loadInt(32) == value) {
|
||||
return cur_key;
|
||||
if (cs!.loadInt(32) == value) {
|
||||
return cur_key!;
|
||||
}
|
||||
}
|
||||
} while (found);
|
||||
|
|
|
@ -21,6 +21,26 @@ fun plus(mutate self: int, y: int): int {
|
|||
|
||||
fun eq<X>(v: X): X { return v; }
|
||||
|
||||
global gTup: [int];
|
||||
global gTens: (int, int);
|
||||
|
||||
@method_id(100)
|
||||
fun testCodegenSimple() {
|
||||
var t1 = [1];
|
||||
t1.0 = 2;
|
||||
debugPrintString("");
|
||||
var t2 = [[1]];
|
||||
t2.0.0 = 2;
|
||||
debugPrintString("");
|
||||
gTup = [1];
|
||||
gTup.0 = 2;
|
||||
debugPrintString("");
|
||||
gTens = (1,2);
|
||||
gTens.1 = 4;
|
||||
debugPrintString("");
|
||||
return (t1, t2, gTup, gTens);
|
||||
}
|
||||
|
||||
@method_id(101)
|
||||
fun test101() {
|
||||
var t = (1, (2, 3), [4, 5, [6, 7]], 8);
|
||||
|
@ -66,8 +86,8 @@ fun test104() {
|
|||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105(x: int, y: int): (tuple, int, (int, int), int, int) {
|
||||
var ab = (createEmptyTuple(), (x, y), tupleSize);
|
||||
fun test105(x: int, y: int): (tuple, int, (int?, int), int, int) {
|
||||
var ab = (createEmptyTuple(), (x as int?, y), tupleSize);
|
||||
ab.0.tuplePush(1);
|
||||
tuplePush(mutate ab.0, 2);
|
||||
ab.1.0 = null;
|
||||
|
@ -78,7 +98,7 @@ fun test105(x: int, y: int): (tuple, int, (int, int), int, int) {
|
|||
|
||||
@method_id(106)
|
||||
fun test106(x: int, y: int) {
|
||||
var ab = [createEmptyTuple(), [x, y], tupleSize];
|
||||
var ab = [createEmptyTuple(), [x as int?, y], tupleSize];
|
||||
ab.0.tuplePush(1);
|
||||
tuplePush(mutate ab.0, 2);
|
||||
ab.1.0 = null;
|
||||
|
@ -158,7 +178,7 @@ fun test114(f: int, s: int) {
|
|||
@method_id(115)
|
||||
fun test115() {
|
||||
var y = [[[[true]]]];
|
||||
return (y, y.0.0.0.0 = !y.0.0.0.0, y.0);
|
||||
return (y, ((((y).0).0).0).0 = !y.0.0.0.0, y.0);
|
||||
}
|
||||
|
||||
@method_id(116)
|
||||
|
@ -213,6 +233,25 @@ fun test121(zero: int) {
|
|||
return t;
|
||||
}
|
||||
|
||||
fun isFirstComponentGt0<T1,T2>(t: (T1, T2)): bool {
|
||||
return t.0 > 0;
|
||||
}
|
||||
|
||||
@method_id(122)
|
||||
fun test122(x: (int, int)) {
|
||||
return (
|
||||
isFirstComponentGt0(x), isFirstComponentGt0((2, beginCell())), isFirstComponentGt0<int,slice?>((0, null)),
|
||||
x.isFirstComponentGt0(), (2, beginCell()).isFirstComponentGt0(), (0, null).isFirstComponentGt0<int,slice?>()
|
||||
);
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test123() {
|
||||
var t = [[10, 20]] as [[int,int]]?;
|
||||
((t!).0).0 = ((t!).0).1 = 100;
|
||||
return t;
|
||||
}
|
||||
|
||||
fun main(){}
|
||||
|
||||
|
||||
|
@ -238,6 +277,58 @@ fun main(){}
|
|||
@testcase | 119 | 1 2 3 4 | 4 1 3
|
||||
@testcase | 120 | | 3 4 [ 5 6 ]
|
||||
@testcase | 121 | 0 | [ 3 ]
|
||||
@testcase | 122 | 1 2 | -1 -1 0 -1 -1 0
|
||||
@testcase | 123 | | [ [ 100 100 ] ]
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
testCodegenSimple PROC:<{
|
||||
//
|
||||
1 PUSHINT // '2=1
|
||||
SINGLE // t1
|
||||
2 PUSHINT // t1 '3=2
|
||||
0 SETINDEX // t1
|
||||
x{} PUSHSLICE // t1 '6
|
||||
STRDUMP DROP
|
||||
1 PUSHINT // t1 '10=1
|
||||
SINGLE // t1 '9
|
||||
SINGLE // t1 t2
|
||||
2 PUSHINT // t1 t2 '11=2
|
||||
OVER // t1 t2 '11=2 t2
|
||||
0 INDEX // t1 t2 '11=2 '14
|
||||
SWAP // t1 t2 '14 '11=2
|
||||
0 SETINDEX // t1 t2 '14
|
||||
0 SETINDEX // t1 t2
|
||||
x{} PUSHSLICE // t1 t2 '17
|
||||
STRDUMP DROP
|
||||
1 PUSHINT // t1 t2 '20=1
|
||||
SINGLE // t1 t2 '18
|
||||
gTup SETGLOB
|
||||
2 PUSHINT // t1 t2 '21=2
|
||||
gTup GETGLOB // t1 t2 '21=2 g_gTup
|
||||
SWAP // t1 t2 g_gTup '21=2
|
||||
0 SETINDEX // t1 t2 g_gTup
|
||||
gTup SETGLOB
|
||||
x{} PUSHSLICE // t1 t2 '25
|
||||
STRDUMP DROP
|
||||
1 PUSHINT // t1 t2 '28=1
|
||||
2 PUSHINT // t1 t2 '26=1 '27=2
|
||||
PAIR
|
||||
gTens SETGLOB
|
||||
4 PUSHINT // t1 t2 g_gTens.1=4
|
||||
gTens GETGLOB
|
||||
UNPAIR // t1 t2 g_gTens.1=4 g_gTens.0 g_gTens.1
|
||||
DROP // t1 t2 g_gTens.1=4 g_gTens.0
|
||||
SWAP // t1 t2 g_gTens.0 g_gTens.1=4
|
||||
PAIR
|
||||
gTens SETGLOB
|
||||
x{} PUSHSLICE // t1 t2 '36
|
||||
STRDUMP DROP
|
||||
gTup GETGLOB // t1 t2 g_gTup
|
||||
gTens GETGLOB
|
||||
UNPAIR // t1 t2 g_gTup g_gTens.0 g_gTens.1
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
|
@ -247,26 +338,6 @@ fun main(){}
|
|||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test104 PROC:<{
|
||||
//
|
||||
5 PUSHINT // '2=5
|
||||
DUP // '2=5 '3=5
|
||||
PAIR // '1
|
||||
SINGLE // m
|
||||
10 PUSHINT // m '5=10
|
||||
20 PUSHINT // m '5=10 '6=20
|
||||
s2 PUSH // m '5=10 '6=20 m
|
||||
0 INDEX // m '10=10 '12=20 '8
|
||||
SWAP // m '10=10 '8 '12=20
|
||||
1 SETINDEX // m '10=10 '8
|
||||
SWAP // m '8 '10=10
|
||||
0 SETINDEX // m '8
|
||||
0 SETINDEX // m
|
||||
...
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
testCodegenIndexPostfix1 PROC:<{
|
||||
|
|
|
@ -18,10 +18,12 @@ fun test1(x: int, y: int) {
|
|||
__expect_type(random() ? x : y, "int");
|
||||
__expect_type(eq(x), "int");
|
||||
__expect_type(eq<int>(x), "int");
|
||||
__expect_type(eq<int>(null), "int");
|
||||
__expect_type(eq<int?>(null), "int?");
|
||||
__expect_type(x as int, "int");
|
||||
__expect_type(+x, "int");
|
||||
__expect_type(~x, "int");
|
||||
__expect_type(x!, "int");
|
||||
__expect_type(x!!!, "int");
|
||||
{
|
||||
var x: slice = beginCell().endCell().beginParse();
|
||||
__expect_type(x, "slice");
|
||||
|
@ -62,9 +64,9 @@ fun test5(x: int) {
|
|||
__expect_type([], "[]");
|
||||
__expect_type([x], "[int]");
|
||||
__expect_type([x, x >= 1], "[int, bool]");
|
||||
__expect_type([x, x >= 1, null as slice], "[int, bool, slice]");
|
||||
__expect_type([x, x >= 1, null as slice?], "[int, bool, slice?]");
|
||||
__expect_type((x, [x], [[x], x]), "(int, [int], [[int], int])");
|
||||
__expect_type(getMyOriginalBalanceWithExtraCurrencies(), "[int, cell]");
|
||||
__expect_type(getMyOriginalBalanceWithExtraCurrencies(), "[int, cell?]");
|
||||
}
|
||||
|
||||
fun test6() {
|
||||
|
@ -84,6 +86,17 @@ fun test7() {
|
|||
// __expect_type(eq<(int, slice)>, "(int, slice) -> (int, slice)");
|
||||
}
|
||||
|
||||
fun alwaysThrows(): never { throw 123; }
|
||||
fun alwaysThrowsNotAnnotated() { throw 123; }
|
||||
fun alwaysThrowsNotAnnotated2() { alwaysThrows(); }
|
||||
|
||||
fun test9() {
|
||||
__expect_type(alwaysThrows(), "never");
|
||||
__expect_type(alwaysThrows, "() -> never");
|
||||
__expect_type(alwaysThrowsNotAnnotated(), "void");
|
||||
__expect_type(alwaysThrowsNotAnnotated2(), "void");
|
||||
}
|
||||
|
||||
|
||||
fun main() {
|
||||
return 0;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
fun main() {
|
||||
var c = 1;
|
||||
(c, c) = (2, 3);
|
||||
var t = createEmptyTuple();
|
||||
t.0 = (1, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr one variable modified twice inside the same expression
|
||||
@stderr a tuple can not have `(int, int)` inside, because it occupies 2 stack slots in TVM, not 1
|
||||
*/
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
fun incThree(mutate a: int, mutate b: int, mutate c: int) {}
|
||||
|
||||
fun main() {
|
||||
var c = [[[1, 2]]];
|
||||
incThree(mutate c.0.0.0, mutate c.0.0.1, mutate c.0.0.0);
|
||||
fun main(cs: slice) {
|
||||
var cb = cs.tupleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr one variable modified twice inside the same expression
|
||||
@stderr referencing a method for `tuple` with object of type `slice`
|
||||
*/
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
global gg: (int, int);
|
||||
|
||||
fun main() {
|
||||
[gg.0, gg.1, gg.0] = [0, 1, 0];
|
||||
var t = createEmptyTuple();
|
||||
var xy = t.0 as (int, int);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr one variable modified twice inside the same expression
|
||||
@stderr a tuple can not have `(int, int)` inside, because it occupies 2 stack slots in TVM, not 1
|
||||
*/
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
global gg: (int, [int, int]);
|
||||
|
||||
fun main() {
|
||||
(gg.1.0, gg.1, gg.1.1) = (0, [1, 2], 3);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr one variable both modified and read inside the same expression
|
||||
*/
|
|
@ -1,9 +0,0 @@
|
|||
fun main() {
|
||||
var ab = (1, 2);
|
||||
(ab, ab.1) = ((2, 3), 4);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr one variable both modified and read inside the same expression
|
||||
*/
|
|
@ -1,9 +0,0 @@
|
|||
fun main() {
|
||||
var t = createEmptyTuple();
|
||||
t.0 = (1, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not put `(int, int)` into a tuple, because it occupies 2 stack slots in TVM, not 1
|
||||
*/
|
|
@ -1,8 +0,0 @@
|
|||
fun main(cs: slice) {
|
||||
var cb = cs.tupleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr referencing a method for `tuple` with object of type `slice`
|
||||
*/
|
|
@ -7,5 +7,5 @@ fun main() {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not put `(int, builder)` into a tuple, because it occupies 2 stack slots in TVM, not 1
|
||||
@stderr a tuple can not have `(int, builder)` inside, because it occupies 2 stack slots in TVM, not 1
|
||||
*/
|
||||
|
|
|
@ -6,5 +6,5 @@ fun failCantDeduceWithoutArgument() {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not deduce X for generic function `f<X>`
|
||||
@stderr too few arguments in call to `f`, expected 2, have 1
|
||||
*/
|
||||
|
|
11
tolk-tester/tests/invalid-generics-13.tolk
Normal file
11
tolk-tester/tests/invalid-generics-13.tolk
Normal file
|
@ -0,0 +1,11 @@
|
|||
fun calcSum<X>(x: X, y: X) { return x + y; }
|
||||
|
||||
fun cantApplyPlusOnNullable() {
|
||||
return calcSum(((0 as int?)), null);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr in function `calcSum<int?>`
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
*/
|
17
tolk-tester/tests/invalid-generics-14.tolk
Normal file
17
tolk-tester/tests/invalid-generics-14.tolk
Normal file
|
@ -0,0 +1,17 @@
|
|||
fun eq<X>(v: X) {}
|
||||
|
||||
fun cantDeduceWhenNotInferred() {
|
||||
// at type inferring (before type checking) they are unknown
|
||||
var (x, y) = 2;
|
||||
|
||||
eq(x as int); // ok (since execution doesn't reach type checking)
|
||||
eq<int>(x); // ok (since execution doesn't reach type checking)
|
||||
eq(x);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr in function `cantDeduceWhenNotInferred`
|
||||
@stderr can not deduce X for generic function `eq<X>`
|
||||
@stderr eq(x);
|
||||
*/
|
|
@ -11,8 +11,7 @@ fun foo<X>(value: X) : X {
|
|||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr while instantiating generic function `foo<slice>`
|
||||
@stderr while instantiating generic function `bar<slice>`
|
||||
@stderr in function `bar<slice>`
|
||||
@stderr can not convert type `int` to return type `slice`
|
||||
@stderr return 1
|
||||
*/
|
||||
|
|
10
tolk-tester/tests/invalid-mutate-18.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-18.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun getNullableTuple(): tuple? { return createEmptyTuple(); }
|
||||
|
||||
fun cantUseLValueUnwrappedNotNull() {
|
||||
tuplePush(mutate getNullableTuple()!, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr function call can not be used as lvalue
|
||||
*/
|
10
tolk-tester/tests/invalid-mutate-19.tolk
Normal file
10
tolk-tester/tests/invalid-mutate-19.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
fun getNullableTuple(): tuple? { return createEmptyTuple(); }
|
||||
|
||||
fun cantUseLValueUnwrappedNotNull() {
|
||||
tuplePush(mutate getNullableTuple()!, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr function call can not be used as lvalue
|
||||
*/
|
13
tolk-tester/tests/invalid-mutate-20.tolk
Normal file
13
tolk-tester/tests/invalid-mutate-20.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
fun acceptMutateNullableTensor(mutate self: (int, int)?) {
|
||||
}
|
||||
|
||||
fun cantModifyTupleIndexWithTypeTransition() {
|
||||
var t = [1, null];
|
||||
t.1.acceptMutateNullableTensor();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not call method for mutate `(int, int)?` with object of type `null`
|
||||
@stderr because mutation is not type compatible
|
||||
*/
|
8
tolk-tester/tests/invalid-never-1.tolk
Normal file
8
tolk-tester/tests/invalid-never-1.tolk
Normal file
|
@ -0,0 +1,8 @@
|
|||
fun invalidNever(): never {
|
||||
if (random()) { throw 123; }
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr a function returning `never` can not have a reachable endpoint
|
||||
*/
|
|
@ -3,6 +3,7 @@ fun failBitwiseNotOnBool() {
|
|||
if (~eq) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
14
tolk-tester/tests/invalid-typing-14.tolk
Normal file
14
tolk-tester/tests/invalid-typing-14.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
fun autoGetIntOrNull() {
|
||||
if (random()) { return 1; }
|
||||
return null;
|
||||
}
|
||||
|
||||
fun testAutoInferredIntOrNull() {
|
||||
var b: builder = autoGetIntOrNull() as builder;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr type `int?` can not be cast to `builder`
|
||||
*/
|
13
tolk-tester/tests/invalid-typing-15.tolk
Normal file
13
tolk-tester/tests/invalid-typing-15.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
fun getNullable4(): int? {
|
||||
return 4;
|
||||
}
|
||||
|
||||
fun testCantSumNullable() {
|
||||
return 1 + getNullable4();
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int` and `int?`
|
||||
*/
|
13
tolk-tester/tests/invalid-typing-16.tolk
Normal file
13
tolk-tester/tests/invalid-typing-16.tolk
Normal file
|
@ -0,0 +1,13 @@
|
|||
@pure
|
||||
fun myDictDeleteStrict(mutate self: cell, keyLen: int, key: int): bool
|
||||
asm(key self keyLen) "DICTIDEL";
|
||||
|
||||
|
||||
fun testCantCallDictMethodsOnNullable(c: cell) {
|
||||
c.beginParse().loadDict().myDictDeleteStrict(16, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not call method for `cell` with object of type `cell?`
|
||||
*/
|
10
tolk-tester/tests/invalid-typing-17.tolk
Normal file
10
tolk-tester/tests/invalid-typing-17.tolk
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
fun testCantUseNullableAsCondition(x: int?) {
|
||||
if (x) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not use `int?` as a boolean condition
|
||||
*/
|
16
tolk-tester/tests/invalid-typing-18.tolk
Normal file
16
tolk-tester/tests/invalid-typing-18.tolk
Normal file
|
@ -0,0 +1,16 @@
|
|||
fun incrementOrSetNull(mutate x: int?) {
|
||||
if (random()) { x! += 1; }
|
||||
else { x = null; }
|
||||
}
|
||||
|
||||
fun cantCallMutateMethodNotNullable() {
|
||||
var x = 1;
|
||||
incrementOrSetNull(mutate x);
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not pass `int` to mutate `int?`
|
||||
@stderr because mutation is not type compatible
|
||||
*/
|
12
tolk-tester/tests/invalid-typing-19.tolk
Normal file
12
tolk-tester/tests/invalid-typing-19.tolk
Normal file
|
@ -0,0 +1,12 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testCantApplyNotNullForAlwaysNull() {
|
||||
var x: int? = getNullableInt();
|
||||
if (x != null) { return 0; }
|
||||
return x! + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr operator `!` used for always null expression
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-20.tolk
Normal file
15
tolk-tester/tests/invalid-typing-20.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testFlowContextAppliedInBinaryOperator() {
|
||||
var x: int? = getNullableInt();
|
||||
var y: int? = getNullableInt();
|
||||
if ((y = null) < y) {
|
||||
return -100;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `<` to `null` and `null`
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-21.tolk
Normal file
14
tolk-tester/tests/invalid-typing-21.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testNeverTypeOccurs() {
|
||||
var x: int? = getNullableInt();
|
||||
if (x == null && x != null) {
|
||||
return x + 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `never` and `int`
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-22.tolk
Normal file
9
tolk-tester/tests/invalid-typing-22.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun testLogicalAndNotConditionDoesntAffect(x: int?) {
|
||||
var gt1 = x != null && x > 1;
|
||||
return x + 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-23.tolk
Normal file
15
tolk-tester/tests/invalid-typing-23.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getTensor(): (int?, int?) { return (5, null); }
|
||||
|
||||
fun testSmartCastsForFieldsDropAfterAssign() {
|
||||
var t = getTensor();
|
||||
if (t.0 != null && t.1 != null) {
|
||||
t = getTensor();
|
||||
return t.0 + t.1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
*/
|
16
tolk-tester/tests/invalid-typing-24.tolk
Normal file
16
tolk-tester/tests/invalid-typing-24.tolk
Normal file
|
@ -0,0 +1,16 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun getTensor(x: int?): (int?, int) { return (x, 0); }
|
||||
|
||||
fun testSmartCastsDropAfterAssign() {
|
||||
var x: int? = 0;
|
||||
var y: int? = 0;
|
||||
(getTensor(x = getNullableInt()).0, getTensor(y = getNullableInt()).0) = (x + y, x - y);
|
||||
return x+y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int?`
|
||||
@stderr x + y, x - y
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-25.tolk
Normal file
14
tolk-tester/tests/invalid-typing-25.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun takeNullableTensor(mutate ij: (int, int)?) { }
|
||||
|
||||
fun testSmartCastsDropAfterMutate() {
|
||||
var x: (int, int)? = (1, 2);
|
||||
return x.0; // ok
|
||||
takeNullableTensor(mutate x);
|
||||
return x.1; // error
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr type `(int, int)?` is not indexable
|
||||
@stderr return x.1
|
||||
*/
|
12
tolk-tester/tests/invalid-typing-26.tolk
Normal file
12
tolk-tester/tests/invalid-typing-26.tolk
Normal file
|
@ -0,0 +1,12 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testAssertThrowIsConditional() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
assert(x != null) throw(y = 10);
|
||||
return x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int` and `int?`
|
||||
*/
|
18
tolk-tester/tests/invalid-typing-27.tolk
Normal file
18
tolk-tester/tests/invalid-typing-27.tolk
Normal file
|
@ -0,0 +1,18 @@
|
|||
fun assignNull2<T1, T2>(mutate x: T1?, mutate y: T2?) {
|
||||
if (false) {
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
}
|
||||
|
||||
fun testSmartCastsDropAfterNullableGeneric() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
x * y; // ok
|
||||
assignNull2(x, y); // treated like assignments to nullable
|
||||
x << y; // error
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `<<` to `int?` and `int?`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-28.tolk
Normal file
15
tolk-tester/tests/invalid-typing-28.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testReassignInRedef() {
|
||||
var t1: int? = getNullableInt();
|
||||
if (t1 != null) {
|
||||
var (t1 redef, t2) = (getNullableInt(), 5);
|
||||
return t1 + t2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `+` to `int?` and `int`
|
||||
*/
|
14
tolk-tester/tests/invalid-typing-29.tolk
Normal file
14
tolk-tester/tests/invalid-typing-29.tolk
Normal file
|
@ -0,0 +1,14 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testTryBodyDontSmartCast() {
|
||||
var x = getNullableInt();
|
||||
try {
|
||||
x = 5;
|
||||
} catch {}
|
||||
return x * 10; // x is not int here; for now, we have no exception edges, assuming it can be anywhere inside try
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `*` to `int?` and `int`
|
||||
*/
|
15
tolk-tester/tests/invalid-typing-30.tolk
Normal file
15
tolk-tester/tests/invalid-typing-30.tolk
Normal file
|
@ -0,0 +1,15 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun testDoWhileCondition() {
|
||||
var (x: int?, y: int?) = (10, 20);
|
||||
do {
|
||||
x = getNullableInt();
|
||||
y = getNullableInt();
|
||||
} while(x == null);
|
||||
return x * y; // x is 100% int, but y is not
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not apply operator `*` to `int` and `int?`
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-44.tolk
Normal file
9
tolk-tester/tests/invalid-typing-44.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun cantAssignIntToTensor() {
|
||||
var (x, y) = 2;
|
||||
x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `int` to a tensor
|
||||
*/
|
9
tolk-tester/tests/invalid-typing-45.tolk
Normal file
9
tolk-tester/tests/invalid-typing-45.tolk
Normal file
|
@ -0,0 +1,9 @@
|
|||
fun cantAssignSizesMismatch() {
|
||||
var [x, y] = [2, 3, 4];
|
||||
x + y;
|
||||
}
|
||||
|
||||
/**
|
||||
@compilation_should_fail
|
||||
@stderr can not assign `[int, int, int]`, sizes mismatch
|
||||
*/
|
|
@ -53,9 +53,8 @@ fun testDict(last: int) {
|
|||
}
|
||||
|
||||
@method_id(105)
|
||||
fun testNotNull(x: int) {
|
||||
// return [x == null, null == x, !(x == null), null == null, +(null != null)];
|
||||
return [x == null, null == x, !(x == null)];
|
||||
fun testNotNull(x: int?) {
|
||||
return [x == null, null == x, !(x == null), null == null, (null != null) as int];
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
|
@ -170,8 +169,8 @@ fun main() {
|
|||
@testcase | 104 | 50 | 3 5 -1
|
||||
@testcase | 104 | 100 | 3 5 5
|
||||
@testcase | 104 | 0 | 3 -1 5
|
||||
@testcase | 105 | 0 | [ 0 0 -1 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 ]
|
||||
@testcase | 105 | 0 | [ 0 0 -1 -1 0 ]
|
||||
@testcase | 105 | null | [ -1 -1 0 -1 0 ]
|
||||
@testcase | 106 | | [ 0 0 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ 0 -1 ]
|
||||
@testcase | 107 | | [ -1 -1 0 -1 ] [ 0 0 0 ] [ -1 -1 -1 ] [ -1 0 ]
|
||||
@testcase | 108 | 1 2 | -1
|
||||
|
|
|
@ -307,7 +307,7 @@ fun main(){}
|
|||
...
|
||||
incrementTwoInPlace CALLDICT // x y sum1
|
||||
-ROT
|
||||
10 PUSHINT // sum1 x y '10=10
|
||||
10 PUSHINT // sum1 x y '11=10
|
||||
incrementTwoInPlace CALLDICT // sum1 x y sum2
|
||||
s1 s3 s0 XCHG3 // x y sum1 sum2
|
||||
}>
|
||||
|
|
28
tolk-tester/tests/never-type-tests.tolk
Normal file
28
tolk-tester/tests/never-type-tests.tolk
Normal file
|
@ -0,0 +1,28 @@
|
|||
fun takeInt(a: int) {}
|
||||
|
||||
@method_id(101)
|
||||
fun test1(x: int?) {
|
||||
if (x == null && x != null) {
|
||||
var y = x;
|
||||
__expect_type(y, "never");
|
||||
__expect_type(y!, "never");
|
||||
// `never` type is assignable to anything, flow won't reach this point
|
||||
var t: (int, int) = x;
|
||||
t = y;
|
||||
takeInt(x);
|
||||
var cb: (int) -> int = x;
|
||||
x as int?;
|
||||
x as (int, int)?;
|
||||
x as never;
|
||||
return x;
|
||||
}
|
||||
return 123;
|
||||
}
|
||||
|
||||
fun main() {
|
||||
__expect_type(test1, "(int?) -> int");
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 101 | null | 123
|
||||
*/
|
|
@ -2,13 +2,13 @@ import "@stdlib/lisp-lists"
|
|||
|
||||
@method_id(101)
|
||||
fun test1() {
|
||||
var numbers: tuple = createEmptyList();
|
||||
var numbers: tuple? = createEmptyList();
|
||||
numbers = listPrepend(1, numbers);
|
||||
numbers = listPrepend(2, numbers);
|
||||
numbers = listPrepend(3, numbers);
|
||||
numbers = listPrepend(4, numbers);
|
||||
var (h: int, numbers redef) = listSplit(numbers);
|
||||
h += listGetHead(numbers);
|
||||
var (h: int, numbers redef) = listSplit(numbers!);
|
||||
h += listGetHead(numbers!);
|
||||
|
||||
_ = null;
|
||||
(_, _) = (null, null);
|
||||
|
@ -22,22 +22,24 @@ fun test1() {
|
|||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test2(x: int) {
|
||||
fun test2(x: int?) {
|
||||
if (null != x) {
|
||||
var y: int = null;
|
||||
var y: int? = null;
|
||||
if (y != null) { return 10; }
|
||||
return y;
|
||||
if (10 < 20) { // always true at runtime (not at compile-time)
|
||||
return y;
|
||||
}
|
||||
}
|
||||
try {
|
||||
return x + 10; // will throw, since not a number
|
||||
return x! + 10; // will throw, since not a number
|
||||
} catch {
|
||||
return -1;
|
||||
}
|
||||
return 100;
|
||||
}
|
||||
|
||||
fun myIsNull(x: int): int {
|
||||
return x == null ? -1 : x;
|
||||
fun myIsNull(x: int?): int {
|
||||
return x == null ? -1 : x!;
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
|
@ -45,14 +47,6 @@ fun test3(x: int) {
|
|||
return myIsNull(x > 10 ? null : x);
|
||||
}
|
||||
|
||||
fun getUntypedNull() {
|
||||
var untyped: null = null;
|
||||
if (true) {
|
||||
return untyped;
|
||||
}
|
||||
return untyped;
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test4(): null {
|
||||
var (_, (_, untyped: null)) = (3, (createEmptyTuple, null));
|
||||
|
@ -62,23 +56,25 @@ fun test4(): null {
|
|||
return untyped;
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test5() {
|
||||
var n: slice = getUntypedNull();
|
||||
return !(null == n) ? n.loadInt(32) : 100;
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test7() {
|
||||
var b = beginCell().storeMaybeRef(null);
|
||||
var s = b.endCell().beginParse();
|
||||
var b = beginCell().storeMaybeRef(null) as builder?;
|
||||
var s = b!.endCell().beginParse();
|
||||
var c = s.loadMaybeRef();
|
||||
return (null == c) as int * 10 + (b != null) as int;
|
||||
}
|
||||
|
||||
fun test8() {
|
||||
__expect_type(null, "null");
|
||||
__expect_type([[null]], "[[null]]");
|
||||
__expect_type(null as tuple?, "tuple?");
|
||||
__expect_type(null as [int]?, "[int]?");
|
||||
__expect_type(((null)) as (int, int)?, "(int, int)?");
|
||||
}
|
||||
|
||||
fun main() {
|
||||
// now, the compiler doesn't optimize this at compile-time, fif codegen contains ifs
|
||||
var i: int = null;
|
||||
// the compiler optimizes this at compile-time
|
||||
var i: int? = null;
|
||||
if (i == null) {
|
||||
return 1;
|
||||
}
|
||||
|
@ -92,7 +88,6 @@ fun main() {
|
|||
@testcase | 103 | 5 | 5
|
||||
@testcase | 103 | 15 | -1
|
||||
@testcase | 104 | | (null)
|
||||
@testcase | 105 | | 100
|
||||
@testcase | 107 | | -11
|
||||
@fif_codegen
|
||||
"""
|
||||
|
@ -120,12 +115,7 @@ fun main() {
|
|||
"""
|
||||
main PROC:<{
|
||||
//
|
||||
PUSHNULL // i
|
||||
ISNULL // '2
|
||||
IFJMP:<{ //
|
||||
1 PUSHINT // '3=1
|
||||
}> //
|
||||
10 PUSHINT // '4=10
|
||||
1 PUSHINT // '3=1
|
||||
}>
|
||||
"""
|
||||
|
||||
|
@ -133,14 +123,14 @@ fun main() {
|
|||
"""
|
||||
test7 PROC:<{
|
||||
...
|
||||
LDOPTREF // b '8 '7
|
||||
LDOPTREF // b '9 '8
|
||||
DROP // b c
|
||||
ISNULL // b '11
|
||||
10 MULCONST // b '13
|
||||
SWAP // '13 b
|
||||
ISNULL // '13 '14
|
||||
NOT // '13 '15
|
||||
ADD // '16
|
||||
NOT // '13 '14
|
||||
ADD // '15
|
||||
}>
|
||||
"""
|
||||
*/
|
||||
|
|
492
tolk-tester/tests/nullable-tensors.tolk
Normal file
492
tolk-tester/tests/nullable-tensors.tolk
Normal file
|
@ -0,0 +1,492 @@
|
|||
fun getNullableInt(): int? { return 5; }
|
||||
|
||||
fun sumOfNullableTensorComponents(t: (int, int)?): int {
|
||||
if (t == null) { return 0; }
|
||||
return t!.0 + t!.1;
|
||||
}
|
||||
|
||||
fun isTensorNull(t: (int, int)?) {
|
||||
return t == null;
|
||||
}
|
||||
|
||||
fun incrementNullableTensorComponents(mutate self: (int, int)?): self {
|
||||
if (self != null) {
|
||||
self!.0 += 1;
|
||||
self!.1 += 1;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
fun incrementTensorComponents(mutate self: (int, int)): self {
|
||||
self.0 += 1;
|
||||
self.1 += 1;
|
||||
return self;
|
||||
}
|
||||
|
||||
fun assignFirstComponent(mutate t: (int, int), first: int) {
|
||||
t!.0 = first;
|
||||
}
|
||||
|
||||
fun assignFirstComponentNullable(mutate t: (int, int)?, first: int) {
|
||||
if (t == null) {
|
||||
t = (first, 0);
|
||||
} else {
|
||||
t!.0 = first;
|
||||
}
|
||||
}
|
||||
|
||||
fun getNullableTensor(firstComponent: int?): (int, int)? {
|
||||
return firstComponent == null ? null : (firstComponent!, 2);
|
||||
}
|
||||
|
||||
fun sumOfTensor(x: (int, int)) {
|
||||
return x.0 + x.1;
|
||||
}
|
||||
|
||||
fun assignNullTo<T>(mutate x: T?) {
|
||||
x = null;
|
||||
}
|
||||
|
||||
fun getTensor12() {
|
||||
return (1,2);
|
||||
}
|
||||
|
||||
@method_id(101)
|
||||
fun test101(): (int, int)? {
|
||||
return (1, 2);
|
||||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test102(): ((int, int)?, (int, int)?) {
|
||||
var t = (1, 2);
|
||||
return (t, null);
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103(t: (int, int)) {
|
||||
var t2: (int, int)? = t;
|
||||
return (sumOfNullableTensorComponents(t), sumOfNullableTensorComponents(t2), sumOfNullableTensorComponents(null), t2);
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test104() {
|
||||
var t1_1: (int, int)? = (1, 2);
|
||||
var t1_2: (int, int)? = t1_1;
|
||||
var t1_3: (int, int)? = t1_1!;
|
||||
var t2_1: (int, int)? = getNullableTensor(null);
|
||||
var t2_2 = t2_1;
|
||||
return (t1_3, t2_2);
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
return (null as (int, slice, cell)?, (1, 2, 3) as (int, int, int)?);
|
||||
}
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
var t: (int?, int?)? = (((((1, 2))) as (int, int)));
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
var ab = (1, 2);
|
||||
var ab2: (int, int)? = ab;
|
||||
return (isTensorNull(ab), isTensorNull(ab2), isTensorNull(null), ab.isTensorNull(), ab2.isTensorNull(), null.isTensorNull());
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108(x1: (int, int)) {
|
||||
incrementTensorComponents(mutate x1);
|
||||
x1.incrementTensorComponents();
|
||||
var x2: (int, int)? = x1;
|
||||
__expect_type(x2, "(int, int)");
|
||||
x2.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x2);
|
||||
__expect_type(x2, "(int, int)?");
|
||||
var x3: (int, int)? = null;
|
||||
__expect_type(x3, "null");
|
||||
x3.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate x3);
|
||||
return (x1, x2, x3);
|
||||
}
|
||||
|
||||
fun isTensorNullGen<T1, T2>(t: (T1, T2)?) {
|
||||
return t == null;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun test109() {
|
||||
var x1 = (1, 2);
|
||||
var x2: (int, int)? = x1;
|
||||
var x3: (int, int)? = x1.1 > 10 ? (1, 2) : null;
|
||||
return (
|
||||
isTensorNullGen(x1), isTensorNullGen(x2), isTensorNullGen<int,int>(null),
|
||||
isTensorNullGen<int,int>(x1), isTensorNullGen<int,int>(x3),
|
||||
x1.isTensorNullGen(), x2.isTensorNullGen(), x3.isTensorNullGen(), null.isTensorNullGen<int,int>()
|
||||
);
|
||||
}
|
||||
|
||||
global g110_1: (int, int);
|
||||
global g110_2: (int, int)?;
|
||||
|
||||
@method_id(110)
|
||||
fun test110() {
|
||||
g110_1 = getNullableTensor(1)!;
|
||||
incrementTensorComponents(mutate g110_1);
|
||||
g110_1.incrementTensorComponents();
|
||||
g110_2 = g110_1;
|
||||
g110_2.incrementNullableTensorComponents().incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate g110_2);
|
||||
var tmp = g110_2;
|
||||
g110_2 = null;
|
||||
g110_2.incrementNullableTensorComponents();
|
||||
incrementNullableTensorComponents(mutate g110_2);
|
||||
return (g110_1, g110_2, tmp);
|
||||
}
|
||||
|
||||
@method_id(111)
|
||||
fun test111() {
|
||||
var x = (1, 2);
|
||||
assignFirstComponent(mutate x, 50);
|
||||
var x2: (int, int)? = null;
|
||||
var x3 = x2 as (int, int)?;
|
||||
assignFirstComponentNullable(mutate x2, 30);
|
||||
assignFirstComponentNullable(mutate x3, 70);
|
||||
g110_1 = (1, 2);
|
||||
g110_2 = null;
|
||||
assignFirstComponent(mutate g110_1, 90);
|
||||
assignFirstComponentNullable(mutate g110_2, 100);
|
||||
return (x.0, x2!.0, x3!.0, g110_1.0, g110_2!.0);
|
||||
}
|
||||
|
||||
@method_id(112)
|
||||
fun test112() {
|
||||
var x: (int, int)? = (10, 20);
|
||||
incrementTensorComponents(mutate x!);
|
||||
x!.incrementTensorComponents();
|
||||
return x;
|
||||
}
|
||||
|
||||
@method_id(113)
|
||||
fun test113() {
|
||||
var t = [1, null]; // t.1 is always null
|
||||
return isTensorNull(t.1);
|
||||
}
|
||||
|
||||
@method_id(114)
|
||||
fun test114(): ((slice, (cell, [int, slice, tuple]))?, slice?, (int?, bool?)?) {
|
||||
var t = [[null]];
|
||||
return (t.0.0, t.0.0, t.0.0);
|
||||
}
|
||||
|
||||
@method_id(115)
|
||||
fun test115() {
|
||||
var tt = getNullableTensor(null);
|
||||
assignFirstComponentNullable(mutate tt, 5);
|
||||
return (
|
||||
getNullableTensor(1)!.incrementTensorComponents(),
|
||||
sumOfNullableTensorComponents(getNullableTensor(1).incrementNullableTensorComponents().incrementNullableTensorComponents()),
|
||||
getNullableTensor(null).incrementNullableTensorComponents(),
|
||||
tt,
|
||||
sumOfNullableTensorComponents(getNullableTensor(null))
|
||||
);
|
||||
}
|
||||
|
||||
@method_id(116)
|
||||
fun test116(returnNull: bool) {
|
||||
var t1: (int, int)? = returnNull ? null : getTensor12();
|
||||
var t2 = returnNull ? null as (int, int)? : getTensor12() as (int, int)?;
|
||||
returnNull ? null : (1, 2);
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(117)
|
||||
fun test117() {
|
||||
var (a, b: (int, int)?, c) = (1, null, 3);
|
||||
return (b, a, c);
|
||||
}
|
||||
|
||||
fun autoInferNullableTensor(a: int?, b: int) {
|
||||
if (a != null) {
|
||||
return (a!, b);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@method_id(118)
|
||||
fun test118(a: int?) {
|
||||
return autoInferNullableTensor(a, 10);
|
||||
}
|
||||
|
||||
@method_id(119)
|
||||
fun test119() {
|
||||
var x: (int, int)? = (1, 2);
|
||||
x = null;
|
||||
var tt: (int, (int, int)?) = (0, (1, 2));
|
||||
tt.1 = null;
|
||||
var third: (int, (int, int)?, int) = (0, (1, 2), 3);
|
||||
third.2 = 100;
|
||||
return (x, tt.1, third.1, third.2);
|
||||
}
|
||||
|
||||
@method_id(120)
|
||||
fun test120(setNull: bool) {
|
||||
var x: (int, int)? = (1, 2);
|
||||
if (setNull) {
|
||||
assignNullTo(mutate x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@method_id(121)
|
||||
fun test121() {
|
||||
var t: [int?, [int?, int?]?] = [1, [2, 3]];
|
||||
t.1 = [3, 4];
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(122)
|
||||
fun test122(setNull: bool) {
|
||||
var t: [int?, [int?, int?]?, int?, [int?, int?]?]? = [1, [2, 3], 4, null];
|
||||
if (setNull) {
|
||||
assignNullTo(mutate t!.1);
|
||||
} else {
|
||||
var rhs = [3, 4];
|
||||
t!!.1 = rhs;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test123() {
|
||||
var t: (int?, (int?, int?)?) = (1, (2, 3));
|
||||
t.1 = (3, 4);
|
||||
return t;
|
||||
}
|
||||
|
||||
@method_id(124)
|
||||
fun test124(setNull: bool) {
|
||||
var t: (int?, (int?, int?)?, int?, (int?, int?)?)? = (1, (2, 3), 4, null);
|
||||
if (setNull) {
|
||||
assignNullTo(mutate t!.1);
|
||||
} else {
|
||||
var rhs = (3, 4);
|
||||
t!!.1 = rhs;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
global g125: int;
|
||||
fun getT125(): (int, (int, int)?, (int?, int)?) { return (g125 += 1, null, null); }
|
||||
|
||||
@method_id(125)
|
||||
fun test125() {
|
||||
g125 = 0;
|
||||
getT125().1 = null;
|
||||
getT125().2 = (1, 2);
|
||||
(getT125()!! as (int, (int, int)?, (int?, int)?)).2 = null;
|
||||
// test that nothing left on a stack
|
||||
return g125;
|
||||
}
|
||||
|
||||
@method_id(126)
|
||||
fun test126() {
|
||||
var tt1: (int, null, int) = (1, null, 2);
|
||||
var (a: int, b: (int, int)?, c: int) = tt1;
|
||||
return (a, b, c);
|
||||
}
|
||||
|
||||
@method_id(127)
|
||||
fun test127(choice: int) {
|
||||
var tt1: (int, null, int) = (1, null, 2);
|
||||
var tt2: (int, (int, int), int) = (1, (2, 3), 4);
|
||||
var tt3: (int, (int, int)?, int) = (1, null, 5);
|
||||
var abc: (int, (int, int)?, int) = choice == 1 ? tt1 : choice == 2 ? tt2 : tt3;
|
||||
return abc;
|
||||
}
|
||||
|
||||
fun get128_1() { return (1, null, 2); }
|
||||
fun get128_2() { return null; }
|
||||
fun get128_3() { return (1, (2, 3), 4); }
|
||||
fun takeT128(abc: (int, (int, int)?, int)?) { return abc; }
|
||||
|
||||
@method_id(128)
|
||||
fun test128(choice: int) {
|
||||
if (choice == 1) {
|
||||
return takeT128(get128_1())!;
|
||||
}
|
||||
if (choice == 2) {
|
||||
return takeT128(get128_2());
|
||||
}
|
||||
return takeT128(get128_3());
|
||||
}
|
||||
|
||||
@method_id(129)
|
||||
fun test129(setNull: bool) {
|
||||
var t: (int?, int?) = (getNullableInt(), getNullableInt());
|
||||
var r1 = (t, t == null, t != null);
|
||||
t = (setNull ? null : 1, setNull ? null : 2);
|
||||
var r2 = (t, t == null, t != null);
|
||||
return (r1, r2);
|
||||
}
|
||||
|
||||
@method_id(130)
|
||||
fun test130(setNull: bool) {
|
||||
var os: (int, (int, int)?) = (1, setNull ? null : (2, 3));
|
||||
return os;
|
||||
}
|
||||
|
||||
fun getEmptyNullableTensor(getNull: bool): ()? {
|
||||
return getNull ? null : ();
|
||||
}
|
||||
|
||||
@method_id(131)
|
||||
fun test131() {
|
||||
var nonNullEmptyT = getEmptyNullableTensor(false);
|
||||
var nullEmptyT = getEmptyNullableTensor(true);
|
||||
var emptyT = nonNullEmptyT!;
|
||||
__expect_type(emptyT, "()");
|
||||
var doubleNulls1 = (null, null) as (()?, ()?);
|
||||
var doubleNulls2 = ((), ()) as (()?, ()?);
|
||||
var doubleNulls3 = ((), ()) as (()?, ()?)?;
|
||||
var stillEmpty = ((), ());
|
||||
return (nonNullEmptyT, 777, nullEmptyT, 777, emptyT, 777, nullEmptyT!, 777, doubleNulls1, doubleNulls2, 777, doubleNulls3, 777, stillEmpty);
|
||||
}
|
||||
|
||||
@method_id(132)
|
||||
fun test132() {
|
||||
var doubleNulls: (()?, ()?) = (getEmptyNullableTensor(true), getEmptyNullableTensor(false));
|
||||
var result = ((null as ()?) == null, (() as ()?) == null, doubleNulls.0 == null, doubleNulls.1 == null);
|
||||
var aln1: int? = (doubleNulls.1 = null);
|
||||
var aln2: null = (doubleNulls.1 = null);
|
||||
return (result, 777, aln1, aln2, doubleNulls.1 == null, doubleNulls);
|
||||
}
|
||||
|
||||
@method_id(133)
|
||||
fun test133() {
|
||||
var x: (int, int)? = (10, 20);
|
||||
return sumOfTensor(x) + x.0 + x.1; // smart casted
|
||||
}
|
||||
|
||||
@method_id(134)
|
||||
fun test134(): (int, int)? {
|
||||
var x: (int, int)? = (10, 20);
|
||||
incrementTensorComponents(mutate x); // smart casted
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
fun getNormalNullableTensorWidth1(vLess100: int?): ([int?], ())? {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return ([vLess100], ()); // such a nullable tensor can store NULL in the same slot
|
||||
}
|
||||
|
||||
fun getTrickyNullableTensorWidth1(vLess100: int?): (int?, ())? {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return (vLess100, ()); // such a nullable tensor requires an extra stack slot for null presence
|
||||
}
|
||||
|
||||
fun getEvenTrickierNullableWidth1(vLess100: int?): ((), (int?, ()), ())? {
|
||||
if (vLess100 != null && vLess100 >= 100) {
|
||||
return null;
|
||||
}
|
||||
return ((), (vLess100, ()), ());
|
||||
}
|
||||
|
||||
@method_id(135)
|
||||
fun test135() {
|
||||
var n1 = getNormalNullableTensorWidth1(10); // ([10], ())
|
||||
var n2 = getNormalNullableTensorWidth1(null); // ([null], ())
|
||||
var n3 = getNormalNullableTensorWidth1(100); // null
|
||||
var t1 = getTrickyNullableTensorWidth1(10); // (10, ())
|
||||
var t2 = getTrickyNullableTensorWidth1(null); // (null, ())
|
||||
var t3 = getTrickyNullableTensorWidth1(100); // null
|
||||
var e1 = getEvenTrickierNullableWidth1(10); // ((), (10, ()), ())
|
||||
var e2 = getEvenTrickierNullableWidth1(null); // ((), (null, (), ())
|
||||
var e3 = getEvenTrickierNullableWidth1(100); // null
|
||||
return (n1, n2, n3, 777, t1, t2, t3, 777, e1, e2, e3, 777,
|
||||
n1 == null, n2 == null, n3 == null, t1 == null, t2 == null, t3 == null, e1 == null, e2 == null, e3 == null, 777,
|
||||
t1!.0 == null, t2!.0 == null, e1!.1.0 == null, e1!.1.1 == null, e2!.1.0 == null, e2!.1.1 == null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun main(){}
|
||||
|
||||
/**
|
||||
@testcase | 101 | | 1 2 -1
|
||||
@testcase | 102 | | 1 2 -1 (null) (null) 0
|
||||
@testcase | 103 | 1 2 | 3 3 0 1 2
|
||||
@testcase | 104 | | 1 2 (null) (null) 0
|
||||
@testcase | 105 | | (null) (null) (null) 0 1 2 3 -1
|
||||
@testcase | 106 | | 1 2
|
||||
@testcase | 107 | | 0 0 -1 0 0 -1
|
||||
@testcase | 108 | 5 6 | 7 8 10 11 -1 (null) (null) 0
|
||||
@testcase | 109 | | 0 0 -1 0 -1 0 0 -1 -1
|
||||
@testcase | 110 | | 3 4 (null) (null) 0 6 7 -1
|
||||
@testcase | 111 | | 50 30 70 90 100
|
||||
@testcase | 112 | | 12 22
|
||||
@testcase | 113 | | -1
|
||||
@testcase | 114 | | (null) (null) (null) 0 (null) (null) (null) 0
|
||||
@testcase | 115 | | 2 3 7 (null) (null) 0 5 0 -1 0
|
||||
@testcase | 116 | -1 | (null) (null) 0 (null) (null) 0
|
||||
@testcase | 116 | 0 | 1 2 -1 1 2 -1
|
||||
@testcase | 117 | | (null) 1 3
|
||||
@testcase | 118 | 5 | 5 10 -1
|
||||
@testcase | 118 | null | (null) (null) 0
|
||||
@testcase | 119 | | (null) (null) 1 2 -1 100
|
||||
@testcase | 120 | -1 | (null) (null) 0
|
||||
@testcase | 120 | 0 | 1 2 -1
|
||||
@testcase | 121 | | [ 1 [ 3 4 ] ]
|
||||
@testcase | 122 | 0 | [ 1 [ 3 4 ] 4 (null) ]
|
||||
@testcase | 122 | -1 | [ 1 (null) 4 (null) ]
|
||||
@testcase | 123 | | 1 3 4 -1
|
||||
@testcase | 124 | 0 | 1 3 4 -1 4 (null) (null) 0
|
||||
@testcase | 124 | -1 | 1 (null) (null) 0 4 (null) (null) 0
|
||||
@testcase | 125 | | 3
|
||||
@testcase | 126 | | 1 (null) 2
|
||||
@testcase | 127 | 1 | 1 (null) (null) 0 2
|
||||
@testcase | 127 | 2 | 1 2 3 -1 4
|
||||
@testcase | 127 | 3 | 1 (null) (null) 0 5
|
||||
@testcase | 128 | 1 | 1 (null) (null) 0 2 -1
|
||||
@testcase | 128 | 2 | (null) (null) (null) (null) (null) 0
|
||||
@testcase | 128 | 3 | 1 2 3 -1 4 -1
|
||||
@testcase | 129 | 0 | 5 5 0 -1 1 2 0 -1
|
||||
@testcase | 129 | -1 | 5 5 0 -1 (null) (null) 0 -1
|
||||
@testcase | 130 | 0 | 1 2 3 -1
|
||||
@testcase | 130 | -1 | 1 (null) (null) 0
|
||||
@testcase | 131 | | -1 777 0 777 777 777 0 0 -1 -1 777 -1 -1 -1 777
|
||||
@testcase | 132 | | -1 0 -1 0 777 (null) (null) -1 0 0
|
||||
@testcase | 133 | | 60
|
||||
@testcase | 134 | | 11 21 -1
|
||||
@testcase | 135 | | [ 10 ] [ (null) ] (null) 777 10 -1 (null) -1 (null) 0 777 10 -1 (null) -1 (null) 0 777 0 0 -1 0 0 -1 0 0 -1 777 0 -1 0 0 -1 0
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
isTensorNull PROC:<{
|
||||
// t.0 t.1 t.NNFlag
|
||||
2 1 BLKDROP2 // t.NNFlag
|
||||
0 EQINT // '3
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
test113 PROC:<{
|
||||
//
|
||||
1 PUSHINT // '2=1
|
||||
PUSHNULL // '2=1 '3
|
||||
PAIR // t
|
||||
1 INDEX // '5
|
||||
PUSHNULL // '5 '6
|
||||
0 PUSHINT // '5 '6 '7=0
|
||||
isTensorNull CALLDICT // '8
|
||||
}>
|
||||
"""
|
||||
*/
|
109
tolk-tester/tests/nullable-types.tolk
Normal file
109
tolk-tester/tests/nullable-types.tolk
Normal file
|
@ -0,0 +1,109 @@
|
|||
|
||||
fun getNullable4(): int? { return 4; }
|
||||
fun getNullableIntNull(): int? asm "PUSHNULL";
|
||||
|
||||
fun eqInt(x: int) { return x; }
|
||||
fun eq<T>(x: T) { return x; }
|
||||
|
||||
fun unwrap<T>(x: T?): T { return x!; }
|
||||
fun intOr0(x: int?): int { return null == x ? 0 : x!; }
|
||||
|
||||
@method_id(101)
|
||||
fun test101(x: int) {
|
||||
var re = x == 0 ? null : 100;
|
||||
return re == null ? re : 200 + getNullable4()!;
|
||||
}
|
||||
|
||||
@method_id(102)
|
||||
fun test102(a: int) {
|
||||
try {
|
||||
throw (123, a > 10 ? null : a);
|
||||
return 0;
|
||||
} catch (excno, arg) {
|
||||
var i = arg as int?;
|
||||
return excno + (i != null ? i!!!!! : -100);
|
||||
}
|
||||
}
|
||||
|
||||
@method_id(103)
|
||||
fun test103(x: int?): (bool, bool, int) {
|
||||
var x_gt_0 = x != null && eqInt(x!) > 0;
|
||||
var x_lt_0 = x != null && eq(x)! < 0;
|
||||
if (x == null) {
|
||||
return (x_gt_0, x_lt_0, 0);
|
||||
}
|
||||
return (x_gt_0, x_lt_0, x!);
|
||||
}
|
||||
|
||||
@method_id(104)
|
||||
fun test104(x: int?) {
|
||||
var x2 = eq(x = 10);
|
||||
var ab = (x2, getNullableIntNull());
|
||||
return (unwrap(ab.0) + (ab.1 == null ? -100 : ab.1!), ab.1);
|
||||
}
|
||||
|
||||
@method_id(105)
|
||||
fun test105() {
|
||||
var xy: (int?, int?) = (5, null);
|
||||
var ab = [1 ? [xy.0, xy.1] : null];
|
||||
ab.0!.0 = intOr0(ab.0!.0);
|
||||
ab.0!.1 = intOr0(ab.0!.1);
|
||||
return ab.0!.0! + ab.0!.1!;
|
||||
}
|
||||
|
||||
global gTup106: tuple?;
|
||||
global gInt106: int?;
|
||||
|
||||
@method_id(106)
|
||||
fun test106() {
|
||||
gInt106 = 0;
|
||||
gInt106! += 5;
|
||||
var int106: int? = 0;
|
||||
var gTup106 = createEmptyTuple();
|
||||
gTup106!.tuplePush(createEmptyTuple());
|
||||
(gTup106!.0 as tuple?)!.tuplePush(0 as int?);
|
||||
tuplePush(mutate gTup106!, gInt106);
|
||||
tuplePush(mutate gTup106!.0, int106! += 1);
|
||||
return (gTup106 == null, null != gTup106, gTup106, gTup106!.0 as tuple?);
|
||||
}
|
||||
|
||||
@method_id(107)
|
||||
fun test107() {
|
||||
var b: builder? = beginCell();
|
||||
b!.storeInt(1, 32).storeInt(2, 32);
|
||||
b = b!.storeInt(3, 32);
|
||||
storeInt(mutate b!, 4, 32);
|
||||
(b! as builder).storeInt(5, 32);
|
||||
return b!.getBuilderBitsCount();
|
||||
}
|
||||
|
||||
@method_id(108)
|
||||
fun test108() {
|
||||
var (a, b: cell?, c) = (1, beginCell().endCell(), 3);
|
||||
if (10>3) { b = null; }
|
||||
return a + (b == null ? 0 : b!.beginParse().loadInt(32)) + c;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun test109() {
|
||||
var a = getNullable4();
|
||||
var b = getNullable4();
|
||||
return ([a, b] = [3, 4], a, b);
|
||||
}
|
||||
|
||||
fun main(x: int?, y: int?) {
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 101 | 0 | (null)
|
||||
@testcase | 101 | -1 | 204
|
||||
@testcase | 102 | 5 | 128
|
||||
@testcase | 102 | 15 | 23
|
||||
@testcase | 103 | 10 | -1 0 10
|
||||
@testcase | 104 | 8 | -90 (null)
|
||||
@testcase | 105 | | 5
|
||||
@testcase | 106 | | 0 -1 [ [ 0 1 ] 5 ] [ 0 1 ]
|
||||
@testcase | 107 | | 160
|
||||
@testcase | 108 | | 4
|
||||
@testcase | 109 | | [ 3 4 ] 3 4
|
||||
*/
|
678
tolk-tester/tests/smart-cast-tests.tolk
Normal file
678
tolk-tester/tests/smart-cast-tests.tolk
Normal file
|
@ -0,0 +1,678 @@
|
|||
// the goal of this file is not only to @testcase results —
|
||||
// but to check that this file compiles
|
||||
|
||||
fun getNullableInt(): int? { return 5; }
|
||||
fun getNullableSlice(): slice? { return null; }
|
||||
fun takeNullableInt(a: int?) {}
|
||||
fun takeNullableSlice(a: slice?) {}
|
||||
fun increment(mutate self: int) { self += 1; }
|
||||
fun assignToInt(mutate self: int, value: int) { self = value; }
|
||||
fun assignToNullableInt(mutate self: int?, value: int) { self = value; }
|
||||
fun sameTensor(t: (int, int)) { return t; }
|
||||
fun sameTensor2(t: (int?, (slice, slice, slice, builder)?)) { return t; }
|
||||
fun eq<T>(v: T) { return v; }
|
||||
fun getTwo<X>(): X { return 2 as X; }
|
||||
|
||||
fun test1(): int {
|
||||
var x = getNullableInt();
|
||||
var y = getNullableInt();
|
||||
if (x != null && y != null) {
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fun test2() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null || y == null) {
|
||||
return null;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test3(): int {
|
||||
var ([x, y]) = [getNullableInt(), getNullableInt()];
|
||||
if (x != null) {
|
||||
if (((y)) != null) {
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
if (random() > -1) {
|
||||
if (y == null) { return -1; }
|
||||
else { return y; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fun test4() {
|
||||
var x = getNullableInt();
|
||||
if (x != null && x > 0) {
|
||||
var x = getNullableInt();
|
||||
if ((x) != null && x + 10 < 0) {
|
||||
var x = getNullableInt();
|
||||
return 10 > 3 && 10 < 10 && x != null && x + 8 > 10;
|
||||
}
|
||||
}
|
||||
if (x != null && x < 1) {
|
||||
return false;
|
||||
}
|
||||
if (x == null && x == null) {
|
||||
__expect_type(x, "null");
|
||||
return true;
|
||||
}
|
||||
return x < x + 3;
|
||||
}
|
||||
|
||||
fun test5() {
|
||||
var (a, (b, c)) = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (a == null) { return -1; }
|
||||
if (!(b != null)) { return -2; }
|
||||
if (random() ? c == null && c == null : c == null) { return -3; }
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
fun test6() {
|
||||
var a: int? = 5;
|
||||
__expect_type(a, "int");
|
||||
__expect_type(a != null ? a : null, "int");
|
||||
__expect_type(a == null ? "" : a, "int");
|
||||
takeNullableInt(a);
|
||||
__expect_type(a, "int");
|
||||
if (random()) {
|
||||
a = null;
|
||||
} else {
|
||||
if (random()) { a = null; }
|
||||
else { a = null; }
|
||||
}
|
||||
__expect_type(a, "null");
|
||||
takeNullableSlice(a); // ok, `slice?` is `slice | null`, here a definitely null
|
||||
var b: int? = true ? null : "sl";
|
||||
__expect_type(b, "null");
|
||||
takeNullableInt(b);
|
||||
takeNullableSlice(b); // same reason
|
||||
var c: int? = 10;
|
||||
__expect_type(c, "int");
|
||||
takeNullableSlice(c = null);
|
||||
}
|
||||
|
||||
fun test7() {
|
||||
var (a, b, c, d) = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
if (a == null && true) { return -1; }
|
||||
if (true && true && 1 && !0 && b == null) { return -2; }
|
||||
if (true ? c == null && (((c))) == null && true : false) { return -3; }
|
||||
if (!true ? random() > 0 : a != null && (d == null && b != null)) { return -4; }
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
fun test8(x: int?, y: int?) {
|
||||
var allGt1 = x != null && x > 1 && y != null && y > 1;
|
||||
var xGtY = x != null && y != null && x > y;
|
||||
var xLtEq0 = x == null || x < 0;
|
||||
(x = 0) < random() || x > 10;
|
||||
return x + 0;
|
||||
}
|
||||
|
||||
fun test9() {
|
||||
var x = getNullableInt();
|
||||
var y = getNullableInt();
|
||||
if (x == null || y == null) {
|
||||
return -1;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test10(): int {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null) {
|
||||
if (y == null) { return -1; }
|
||||
__expect_type(x, "null");
|
||||
__expect_type(y, "int");
|
||||
return y;
|
||||
}
|
||||
if (y == null) {
|
||||
return x;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test11() {
|
||||
var [x, y] = [getNullableInt(), getNullableInt()];
|
||||
if (random()) { return x == null || y == null ? -1 : x + y; }
|
||||
if (true && (x == null || y == null) && !!true) { return 0; }
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test12() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (random() ? x == null || y == null : x == null || y == null) { return -1; }
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test13() {
|
||||
var x: int? = getNullableInt();
|
||||
var y: int? = 10;
|
||||
var z = getNullableInt();
|
||||
var w = getNullableInt();
|
||||
beginCell().storeInt(x!, 32).storeInt(x = getNullableInt()!, 32).storeInt(x, 32)
|
||||
.storeInt(y, 32).storeInt(z = 10, 32).storeInt(x + y + z, 32)
|
||||
.storeInt(w == null ? -1 : w, 32).storeInt(!(null == w) ? w : -1, 32);
|
||||
}
|
||||
|
||||
fun test14() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (x == null) {
|
||||
x = 0;
|
||||
}
|
||||
if (y == null) {
|
||||
if (random()) { return 0; }
|
||||
else { y = 0; }
|
||||
}
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test20() {
|
||||
var t = (getNullableInt(), getNullableInt());
|
||||
if (t.0 != null && t.1 != null) {
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "int");
|
||||
return t.0 + t.1;
|
||||
}
|
||||
t.0 = 10;
|
||||
if (t.1 == null) {
|
||||
t.1 = 20;
|
||||
}
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "int");
|
||||
return t.0 + t.1;
|
||||
}
|
||||
|
||||
fun test21() {
|
||||
var t = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 != null && t.1.0 != null) {
|
||||
if (t.1.1 != null) { return t.0 + t.1.0 + t.1.1; }
|
||||
return t.0 + t.1.0;
|
||||
}
|
||||
if (t.0 != null) {
|
||||
return t.0 + 0;
|
||||
}
|
||||
__expect_type(t.0, "null");
|
||||
__expect_type(t.1.0, "int?");
|
||||
return t.1.0 == null ? -1 : t.1.0 + 0;
|
||||
}
|
||||
|
||||
fun test22() {
|
||||
var t = (getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 == null || t.1.0 == null || t.1.1 == null) {
|
||||
return -1;
|
||||
}
|
||||
return t.0 + t.1.0 + t.1.1;
|
||||
}
|
||||
|
||||
@method_id(123)
|
||||
fun test23() {
|
||||
var (x: int?, y: int?, z: int?) = (getNullableInt(), getNullableInt(), getNullableInt());
|
||||
((x = 1, 0).0, (y = 2, 1).0) = (3, z = 4);
|
||||
return x + y + z;
|
||||
}
|
||||
|
||||
@method_id(124)
|
||||
fun test24(x: int?) {
|
||||
if (x == null) {
|
||||
__expect_type(x, "null");
|
||||
assignToNullableInt(mutate x, 10);
|
||||
__expect_type(x, "int?");
|
||||
x.assignToNullableInt(x! + 5);
|
||||
} else {
|
||||
__expect_type(x, "int");
|
||||
increment(mutate x);
|
||||
x.increment();
|
||||
__expect_type(x, "int");
|
||||
}
|
||||
__expect_type(x, "int?");
|
||||
return x;
|
||||
}
|
||||
|
||||
fun test25() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt());
|
||||
x.0 = x.2 = random();
|
||||
return (x.0) + ((x.2));
|
||||
}
|
||||
|
||||
fun test26() {
|
||||
var x = [getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(),
|
||||
getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt()];
|
||||
if (~(x.0 = random())) { return; }
|
||||
if ((x.1 = random()) < (x.2 = random())) { return; }
|
||||
else if (!(x.2 <=> (x.3 = random()))) { return; }
|
||||
x.5 = (x.4 = random()) ? (x.6 = random()) : (x.6 = random());
|
||||
if ((x.7 = random()) as int) { return; }
|
||||
if (((((x.8 = random()) != null)))) { return; }
|
||||
if ([x.1, (x.9 = random())!].1) { return; }
|
||||
val result = x.0+x.1+x.2+x.3+x.4+x.5+x.6+x.7+x.8+x.9;
|
||||
}
|
||||
|
||||
fun test27() {
|
||||
var (x, _) = ([getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(),
|
||||
getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt()], []);
|
||||
+(x.0 = random());
|
||||
x.0 += [((x.1 = random()) < (x.2 = random() + x.1)) as int].0;
|
||||
!(x.2 <=> (x.3 = random() + x.2));
|
||||
x.5 = (x.4 = random()) ? (x.6 = random()) : (x.6 = random());
|
||||
(x.7 = random()) as int;
|
||||
(((((x.8 = random()) != null))));
|
||||
[x.1, (x.9 = random())!].1;
|
||||
return x.0+x.1+x.2+x.3+x.4+x.5+x.6+x.7+x.8+x.9;
|
||||
}
|
||||
|
||||
fun test28() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
__expect_type((x.0 = random(), x.0 += (x.1 = random()) as int, !(x.1 <=> (x.2 = random() + x.0)) == null, (x.3 = random()) ? x.3 : (!x.3) as int),
|
||||
"(int, int, bool, int)");
|
||||
}
|
||||
|
||||
fun test29() {
|
||||
var x = (getNullableInt(), getNullableInt(), getNullableInt(), getNullableInt());
|
||||
__expect_type([x.0 = random(), ((x.0 += (x.1 = random()) as int)), !(x.1 <=> (x.2 = random() + x.0)) == null, (x.3 = random()) ? x.3 : (!x.3) as int],
|
||||
"[int, int, bool, int]");
|
||||
}
|
||||
|
||||
@method_id(130)
|
||||
fun test30(initial5: bool) {
|
||||
var t: (int?, (int?, (int?, int?))) = initial5
|
||||
? (getNullableInt(), (getNullableInt(), (getNullableInt(), getNullableInt())))
|
||||
: (null, (null, (null, null)));
|
||||
if (t.0 == null || t.1.0 == null || t.1.1.0 == null || t.1.1.1 == null) {
|
||||
if (t.1.0 == null || t.1.1.0 == null) {
|
||||
if (t.1.1.0 == null) {
|
||||
t.1.1.0 = 4;
|
||||
}
|
||||
__expect_type(t.1.1.0, "int");
|
||||
__expect_type(t.1.1.1, "int?");
|
||||
__expect_type(t.1.0, "int?");
|
||||
t.1.1.1 = 3;
|
||||
t.1.0 = 2;
|
||||
__expect_type(t.1.1.1, "int");
|
||||
__expect_type(t.1.0, "int");
|
||||
}
|
||||
if (((((t.1.1.1)))) != null) {}
|
||||
else { t.1.1.1 = 3; }
|
||||
t.0 = 1;
|
||||
}
|
||||
return t.0 + t.1.0 + t.1.1.0 + t.1.1.1;
|
||||
}
|
||||
|
||||
fun test31() {
|
||||
var t = (getNullableInt(), getNullableInt());
|
||||
t.0 == null ? (t.0, t.1) = (1, 2) : (t.1, t.0) = (4, 3);
|
||||
return t.0 + t.1;
|
||||
}
|
||||
|
||||
@method_id(132)
|
||||
fun test32() {
|
||||
var t: (int?, (int?, int?)?, (int?, int?)) = (getNullableInt(), (getNullableInt(), getNullableInt()), (getNullableInt(), getNullableInt()));
|
||||
if (t.0 == null) { return -1; }
|
||||
t.1 != null && t.1.0 == null ? t.1 = (1, 2) : t.1 = (3, 4);
|
||||
if (t.2.1 != null) { t.2.0 = 1; t.2.1 = 2; }
|
||||
else { [t.2.0, t.2.1] = [3, 4]; }
|
||||
return t.0 + t.1.0! + t.1.1! + t.2.0 + t.2.1;
|
||||
}
|
||||
|
||||
@method_id(133)
|
||||
fun test33(): int {
|
||||
var x = getNullableInt();
|
||||
repeat (eq(x = 5)) {
|
||||
__expect_type(x, "int");
|
||||
increment(mutate x);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
fun test34() {
|
||||
var (x, y) = (getNullableInt(), getNullableInt());
|
||||
if (random()) { throw (x = 1, y = 2); }
|
||||
else { throw (x = 3, y = (1, getNullableInt()!).1); }
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test35() {
|
||||
var (x, y, z, t) = (getNullableInt(), getNullableInt(), getNullableInt(), (getNullableInt(), getNullableInt()));
|
||||
assert (x != null, 404);
|
||||
assert (t.0 != null && true && !(t.1 == null) && !(z = 4)) throw (y = 404);
|
||||
__expect_type(y, "int?");
|
||||
return x + t.0 + t.1 + z;
|
||||
}
|
||||
|
||||
fun test36() {
|
||||
var x = getNullableInt();
|
||||
assert (x == null, x + 0); // check that x is int there
|
||||
__expect_type(x, "null");
|
||||
}
|
||||
|
||||
fun test37() {
|
||||
var (x, code) = (getNullableInt()!, getNullableInt());
|
||||
try {
|
||||
} catch(code) {
|
||||
x = 20;
|
||||
return x + code; // code is scoped
|
||||
}
|
||||
return code == null ? x : x + code;
|
||||
}
|
||||
|
||||
fun assignNull2<T1, T2>(mutate x: T1?, mutate y: T2?) {
|
||||
x = null;
|
||||
y = null;
|
||||
}
|
||||
|
||||
fun test38() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
__expect_type(x, "int");
|
||||
__expect_type(y, "int");
|
||||
assignNull2<int, int>(mutate x, mutate y);
|
||||
__expect_type(x, "int?");
|
||||
__expect_type(y, "int?");
|
||||
if (x != null) {
|
||||
if (y == null) { return -1; }
|
||||
return x + y;
|
||||
}
|
||||
var t: (int?, slice?) = (null, null);
|
||||
if (!false) { t.0 = 1; }
|
||||
if (true) { t.1 = beginCell().endCell().beginParse(); }
|
||||
__expect_type(t.0, "int");
|
||||
__expect_type(t.1, "slice");
|
||||
t.0 + t.1.loadInt(32);
|
||||
assignNull2(mutate t.0, mutate t.1);
|
||||
__expect_type(t.0, "int?");
|
||||
__expect_type(t.1, "slice?");
|
||||
t.0 != null && t.1 != null ? t.0 + loadInt(mutate t.1, 32) : -1;
|
||||
return t.0 != null && t.1 != null ? t.0 + loadInt(mutate t.1, 32) : -1;
|
||||
}
|
||||
|
||||
@method_id(139)
|
||||
fun test39() {
|
||||
var x: (int?, int?)? = (4, null);
|
||||
x.1 = 10;
|
||||
x.1 += 1;
|
||||
x!.1 += 1;
|
||||
return (x!.0! + x.1);
|
||||
}
|
||||
|
||||
@method_id(140)
|
||||
fun test40(second: int?) {
|
||||
var x: (int?, int?)? = (4, second);
|
||||
if (x.1 != null) {
|
||||
val result = x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
if (x!.1 != null) {
|
||||
val result = x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
if (!(x!!.1 != null)) {
|
||||
return -1;
|
||||
}
|
||||
return x.1 + x!.1 + x!!.1 + x.1! + x!!.1!!;
|
||||
}
|
||||
|
||||
@method_id(141)
|
||||
fun test41() {
|
||||
var t: (int, int)? = null;
|
||||
return sameTensor(t = (1, 2));
|
||||
}
|
||||
|
||||
@method_id(142)
|
||||
fun test42() {
|
||||
var t: (int?, (int?, (int, int)?)?) = (getNullableInt(), (1, (2, 3)));
|
||||
t.1 = (3,null);
|
||||
__expect_type(t.1, "(int?, (int, int)?)");
|
||||
__expect_type(t, "(int?, (int?, (int, int)?)?)");
|
||||
return (t, t.1);
|
||||
}
|
||||
|
||||
@method_id(143)
|
||||
fun test43() {
|
||||
var t1: ((int, int), int?) = ((1, 2), 3);
|
||||
var t2: ((int?, int?), (int?,int?)?) = ((null, null), (null, 5));
|
||||
t2.0 = t1.0 = (10, 11);
|
||||
t2.1 = t1.1 = null;
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(144)
|
||||
fun test44() {
|
||||
var t1: ((int, int), int?) = ((1, 2), 3);
|
||||
var t2: ((int?, int?), (int?,int?)?) = ((null, null), (null, 5));
|
||||
t1.0 = t2.0 = (10, 11);
|
||||
t1.1 = t2.1 = null;
|
||||
__expect_type(t1, "((int, int), int?)");
|
||||
__expect_type(t2, "((int?, int?), (int?, int?)?)");
|
||||
return (t1, t2);
|
||||
}
|
||||
|
||||
@method_id(145)
|
||||
fun test45() {
|
||||
var t: (int?, (int?, (int, int)?)?) = (getNullableInt(), (1, (2, 3)));
|
||||
var t2 = sameTensor2(t.1 = (3,null));
|
||||
return (t, t2, t.1);
|
||||
}
|
||||
|
||||
fun autoInfer46() {
|
||||
var t1: int? = 3;
|
||||
var t2: (int, int)? = (4, 5);
|
||||
__expect_type(t1, "int");
|
||||
__expect_type(t2, "(int, int)");
|
||||
return (t1, t2); // proven to be not null, inferred (int, (int,int))
|
||||
}
|
||||
|
||||
@method_id(146)
|
||||
fun test46() {
|
||||
var r46_1: (int, (int,int)) = autoInfer46();
|
||||
var r46_2: (int, (int,int)?) = autoInfer46();
|
||||
return (r46_1, r46_2);
|
||||
}
|
||||
|
||||
@method_id(147)
|
||||
fun test47() {
|
||||
var t1: int? = 3;
|
||||
var t2: (int, int)? = (4, 5);
|
||||
t1 = t2 = null;
|
||||
__expect_type(t1, "null");
|
||||
__expect_type(t2, "null");
|
||||
var result = (t1, t2); // proven to be always null, inferred (null, null), 2 slots on a stack
|
||||
return (result, 100, result.1, 100, t2 as (int, int)?);
|
||||
}
|
||||
|
||||
fun test48() {
|
||||
var t1: int? = getNullableInt();
|
||||
if (t1 != null) {
|
||||
var (t1 redef, t2) = (10, 5);
|
||||
return t1 + t2;
|
||||
var t2 redef = getNullableInt()!;
|
||||
return t1 + t2;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
fun test49(x: int?) {
|
||||
while (x == null) {
|
||||
x = getNullableInt();
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
fun test50() {
|
||||
var (x: int?, y: int?) = (1, 2);
|
||||
do {
|
||||
x = getNullableInt();
|
||||
y = getNullableInt();
|
||||
} while (x == null || y == null);
|
||||
return x + y;
|
||||
}
|
||||
|
||||
fun test51() {
|
||||
while (true) { return; }
|
||||
// test that no error "control reaches end of function"
|
||||
}
|
||||
|
||||
fun test52() {
|
||||
do { } while (true);
|
||||
}
|
||||
|
||||
fun test53() {
|
||||
var x1: int? = getNullableInt();
|
||||
var x2: int? = 5;
|
||||
var x3: int? = 5;
|
||||
var x10: int? = null;
|
||||
var x11: int? = 5;
|
||||
var x12: int? = 5;
|
||||
while (x1 != null) {
|
||||
__expect_type(x1, "int"); // because condition
|
||||
__expect_type(x2, "int?"); // because re-assigned
|
||||
__expect_type(x3, "int?"); // because re-assigned
|
||||
__expect_type(x10, "null");
|
||||
__expect_type(x11, "int");
|
||||
x1 = getNullableInt();
|
||||
__expect_type(x1, "int?");
|
||||
assignToNullableInt(mutate x2, 5);
|
||||
x3.assignToNullableInt(5);
|
||||
x11 = 10;
|
||||
assignToInt(mutate x12, 5);
|
||||
}
|
||||
__expect_type(x1, "null");
|
||||
__expect_type(x2, "int?");
|
||||
__expect_type(x3, "int?");
|
||||
}
|
||||
|
||||
fun test54() {
|
||||
var x1: int? = null;
|
||||
var x2: int? = 5;
|
||||
var x3: int? = 5;
|
||||
var x10: int? = null;
|
||||
var x11: int? = 5;
|
||||
var x12: int? = 5;
|
||||
do {
|
||||
__expect_type(x1, "int?"); // because re-assigned
|
||||
__expect_type(x2, "int?"); // because re-assigned
|
||||
__expect_type(x3, "int?"); // because re-assigned
|
||||
__expect_type(x10, "null");
|
||||
__expect_type(x11, "int");
|
||||
x1 = getNullableInt();
|
||||
__expect_type(x1, "int?");
|
||||
assignToNullableInt(mutate x2, 5);
|
||||
if (random()) { x3.assignToNullableInt(5); }
|
||||
x11 = 10;
|
||||
assignToInt(mutate x12, 5);
|
||||
} while (x1 != null);
|
||||
__expect_type(x1, "null");
|
||||
__expect_type(x2, "int?");
|
||||
__expect_type(x3, "int?");
|
||||
}
|
||||
|
||||
fun eq55<T>(v: T) { return v; }
|
||||
|
||||
fun test55() {
|
||||
var x: int? = 4;
|
||||
while (true) {
|
||||
// currently, generic functions are instantiated at the type inferring step
|
||||
// in case of loops, type inferring is re-enterable
|
||||
// first iteration: x is int, eq<int> instantiated
|
||||
// second (final) iteration: x is int?, eq<int?> instantiated
|
||||
// (checked via codegen)
|
||||
eq55(x);
|
||||
__expect_type(x, "int?"); // types are checked (unlike generics instantiated) after inferring
|
||||
x = random() ? 1 : null;
|
||||
}
|
||||
__expect_type(x, "int?");
|
||||
}
|
||||
|
||||
fun test56() {
|
||||
var i: int? = null;
|
||||
var (j: int?, k: int?) = (null, null);
|
||||
__expect_type(i, "null");
|
||||
__expect_type(k, "null");
|
||||
i = getTwo();
|
||||
[j, ((k))] = [getTwo(), ((getTwo()))];
|
||||
__expect_type(i, "int?");
|
||||
__expect_type(j, "int?");
|
||||
__expect_type(k, "int?");
|
||||
}
|
||||
|
||||
fun test57(mutate x: int?): int {
|
||||
if (x == null) { x = 5; }
|
||||
else {
|
||||
if (x < 10) { x = 10; }
|
||||
else { x = 20; }
|
||||
}
|
||||
if (x != null) {
|
||||
return 123;
|
||||
}
|
||||
__expect_type(x, "int");
|
||||
// no "return" needed, because end of function is unreachable
|
||||
}
|
||||
|
||||
@method_id(158)
|
||||
fun test58() {
|
||||
var (x1, x2: int?) = (getNullableInt(), null);
|
||||
return (test57(mutate x1), x1, test57(mutate x2), x2);
|
||||
}
|
||||
|
||||
fun test59() {
|
||||
var (x1: int?, x2, x3) = (getNullableInt()!, getNullableInt(), 5);
|
||||
if ((x2 = x3) != null) {
|
||||
__expect_type(x2, "int");
|
||||
}
|
||||
__expect_type(x2, "int");
|
||||
if ((x2 = getNullableInt()) != null) {
|
||||
__expect_type(x2, "int");
|
||||
}
|
||||
__expect_type(x2, "int?");
|
||||
if (((x1) = x2) == null) {
|
||||
return;
|
||||
}
|
||||
__expect_type(x1, "int");
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun main(x: int?): int {
|
||||
return x == null ? -1 : x;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 1 | 1
|
||||
@testcase | 123 | | 7
|
||||
@testcase | 124 | 4 | 6
|
||||
@testcase | 124 | null | 15
|
||||
@testcase | 130 | -1 | 20
|
||||
@testcase | 130 | 0 | 10
|
||||
@testcase | 132 | | 15
|
||||
@testcase | 133 | | 10
|
||||
@testcase | 139 | | 16
|
||||
@testcase | 140 | 5 | 25
|
||||
@testcase | 141 | | 1 2
|
||||
@testcase | 142 | | 5 3 (null) (null) 0 -1 3 (null) (null) 0
|
||||
@testcase | 143 | | 10 11 (null) 10 11 (null) (null) 0
|
||||
@testcase | 144 | | 10 11 (null) 10 11 (null) (null) 0
|
||||
@testcase | 145 | | 5 3 (null) (null) 0 -1 3 (null) (null) (null) (null) 0 3 (null) (null) 0
|
||||
@testcase | 146 | | 3 4 5 3 4 5 -1
|
||||
@testcase | 147 | | (null) (null) 100 (null) 100 (null) (null) 0
|
||||
@testcase | 158 | | 123 10 123 5
|
||||
|
||||
@stderr warning: expression of type `int` is always not null, this condition is always true
|
||||
@stderr warning: unreachable code
|
||||
@stderr var t2 redef = getNullableInt()!;
|
||||
|
||||
@fif_codegen eq55<int?> PROC:<{
|
||||
@fif_codegen eq55<int> PROC:<{
|
||||
*/
|
|
@ -164,6 +164,78 @@ fun test109(): (int, int) {
|
|||
return (g_reg, l_reg);
|
||||
}
|
||||
|
||||
fun alwaysThrow123(): never {
|
||||
throw 123;
|
||||
}
|
||||
|
||||
fun alwaysThrowX(x: int): never {
|
||||
if (x > 10) { throw (x, beginCell()); }
|
||||
else { throw (x, null); }
|
||||
}
|
||||
|
||||
fun anotherNever(throw123: bool): never {
|
||||
if (throw123) { alwaysThrow123(); }
|
||||
alwaysThrowX(456);
|
||||
}
|
||||
|
||||
fun testCodegen1(x: int) {
|
||||
if (x > 10) {
|
||||
throw 123;
|
||||
anotherNever(true); // unreachable, will be dropped
|
||||
}
|
||||
else if (x < 10) {
|
||||
throw x;
|
||||
return -123; // unreachable, will be dropped
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fun testCodegen2(x: int) {
|
||||
if (x > 10) {
|
||||
alwaysThrow123();
|
||||
anotherNever(true); // unreachable, will be dropped
|
||||
}
|
||||
else if (x < 10) {
|
||||
anotherNever(false);
|
||||
return -123; // unreachable, will be dropped
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@method_id(110)
|
||||
fun test110(b: bool) {
|
||||
try {
|
||||
if (b == true) { testCodegen1(100); }
|
||||
testCodegen1(5);
|
||||
return -1;
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
@method_id(111)
|
||||
fun test111(b: bool) {
|
||||
try {
|
||||
if (b == true) { testCodegen2(100); }
|
||||
testCodegen2(5);
|
||||
return -1;
|
||||
} catch (ex) {
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
fun mySetCode(newCode: slice): void
|
||||
asm "SETCODE";
|
||||
|
||||
fun testCodegen3(numberId: int, paramVal: cell) {
|
||||
if (numberId == -1000) {
|
||||
var cs = paramVal.beginParse();
|
||||
mySetCode(cs);
|
||||
throw 0;
|
||||
}
|
||||
paramVal.beginParse();
|
||||
}
|
||||
|
||||
fun main() {
|
||||
}
|
||||
|
||||
|
@ -187,6 +259,65 @@ fun main() {
|
|||
@testcase | 107 | 5 | 5
|
||||
@testcase | 107 | 20 | 20
|
||||
@testcase | 108 | | 0
|
||||
@testcase | 109 | | 10 10
|
||||
@testcase | 110 | -1 | 123
|
||||
@testcase | 110 | 0 | 5
|
||||
@testcase | 111 | -1 | 123
|
||||
@testcase | 111 | 0 | 456
|
||||
|
||||
@code_hash 39307974281105539319288356721945232226028429128341177951717392648324358675585
|
||||
@code_hash 57361460846265694653029920796509802052573595128418810728101968091567195330515
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
testCodegen1 PROC:<{
|
||||
// x
|
||||
DUP // x x
|
||||
10 GTINT // x '2
|
||||
IFJMP:<{ // x
|
||||
123 THROW
|
||||
}> // x
|
||||
DUP // x x
|
||||
10 LESSINT // x '6
|
||||
IFJMP:<{ // x
|
||||
THROWANY
|
||||
}> // x
|
||||
DROP //
|
||||
0 PUSHINT // '8=0
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
testCodegen2 PROC:<{
|
||||
// x
|
||||
DUP // x x
|
||||
10 GTINT // x '2
|
||||
IFJMP:<{ // x
|
||||
DROP //
|
||||
alwaysThrow123 CALLDICT
|
||||
}> // x
|
||||
10 LESSINT // '5
|
||||
IFJMP:<{ //
|
||||
FALSE // '6
|
||||
anotherNever CALLDICT
|
||||
}> //
|
||||
0 PUSHINT // '8=0
|
||||
}>
|
||||
"""
|
||||
|
||||
@fif_codegen
|
||||
"""
|
||||
testCodegen3 PROC:<{
|
||||
// numberId paramVal
|
||||
SWAP
|
||||
-1000 PUSHINT // paramVal numberId '2=-1000
|
||||
EQUAL // paramVal '3
|
||||
IFJMP:<{ // paramVal
|
||||
CTOS // cs
|
||||
SETCODE
|
||||
0 THROW
|
||||
}> // paramVal
|
||||
DROP //
|
||||
}>
|
||||
"""
|
||||
*/
|
||||
|
|
22
tolk-tester/tests/unreachable-3.tolk
Normal file
22
tolk-tester/tests/unreachable-3.tolk
Normal file
|
@ -0,0 +1,22 @@
|
|||
fun main(x: int?) {
|
||||
if (x != null && x == null) {
|
||||
return 1 + 2;
|
||||
}
|
||||
if (x == null) {
|
||||
return -1;
|
||||
}
|
||||
if (x != null) {
|
||||
return -2;
|
||||
}
|
||||
return 3 + 4;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | 5 | -2
|
||||
@testcase | 0 | null | -1
|
||||
|
||||
@stderr warning: variable `x` of type `int` is always not null
|
||||
@stderr if (x != null)
|
||||
@stderr warning: unreachable code
|
||||
@stderr return 3 + 4
|
||||
*/
|
24
tolk-tester/tests/unreachable-4.tolk
Normal file
24
tolk-tester/tests/unreachable-4.tolk
Normal file
|
@ -0,0 +1,24 @@
|
|||
fun alwaysThrows(): never {
|
||||
throw 456;
|
||||
}
|
||||
|
||||
fun testUnreachable(x: int) {
|
||||
if (x) { throw 123; }
|
||||
else { alwaysThrows(); }
|
||||
return 1;
|
||||
}
|
||||
|
||||
fun main() {
|
||||
try {
|
||||
testUnreachable(100);
|
||||
throw 80;
|
||||
} catch (excNo) {
|
||||
return excNo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | | 123
|
||||
@stderr warning: unreachable code
|
||||
@stderr return 1;
|
||||
*/
|
|
@ -27,8 +27,8 @@ fun test1(): int {
|
|||
var demo_var: int = demo_10;
|
||||
var demo_slice: int = demo_20;
|
||||
if (demo_var > 0) {
|
||||
var demo_var: tuple = null;
|
||||
var demo_slice: tuple = null;
|
||||
var demo_var: tuple? = null;
|
||||
var demo_slice: tuple? = null;
|
||||
}
|
||||
return demo_var + demo_slice;
|
||||
}
|
||||
|
|
|
@ -138,6 +138,43 @@ fun testIndexedAccessApply() {
|
|||
return functions2.0(functions1.1(b)).loadInt(32);
|
||||
}
|
||||
|
||||
fun getNullable4(): int? { return 4; }
|
||||
fun myBeginCell(): builder? asm "NEWC";
|
||||
|
||||
@method_id(108)
|
||||
fun testCallingNotNull() {
|
||||
var n4: () -> int? = getNullable4;
|
||||
var creator: (() -> builder?)? = myBeginCell;
|
||||
var end2: [int, (builder -> cell)?] = [0, endCell];
|
||||
var c: cell = end2.1!((creator!()!)!.storeInt(getNullable4()!, 32));
|
||||
return c.beginParse().loadInt(32);
|
||||
}
|
||||
|
||||
fun sumOfTensorIfNotNull(t: (int, int)?) {
|
||||
if (t == null) { return 0; }
|
||||
return t!.0 + t!.1;
|
||||
}
|
||||
|
||||
@method_id(109)
|
||||
fun testTypeTransitionOfVarCall() {
|
||||
var summer = sumOfTensorIfNotNull;
|
||||
var hh1 = [1, null];
|
||||
var tt1 = (3, 4);
|
||||
return (summer(null), summer((1,2)), summer(hh1.1), summer(tt1));
|
||||
}
|
||||
|
||||
fun makeTensor(x1: int, x2: int, x3: int, x4: int, x5: int) {
|
||||
return (x1, x2, x3, x4, x5);
|
||||
}
|
||||
|
||||
fun eq<T>(x: T): T { return x; }
|
||||
|
||||
@method_id(110)
|
||||
fun testVarsModificationInsideVarCall(x: int) {
|
||||
var cb = makeTensor;
|
||||
return x > 3 ? cb(x, x += 5, eq(x *= x), x, eq(x)) : null;
|
||||
}
|
||||
|
||||
fun main() {}
|
||||
|
||||
/**
|
||||
|
@ -148,4 +185,8 @@ fun main() {}
|
|||
@testcase | 105 | | 1
|
||||
@testcase | 106 | | 1 1 [ 2 ] [ 2 ]
|
||||
@testcase | 107 | | 65537
|
||||
@testcase | 108 | | 4
|
||||
@testcase | 109 | | 0 3 0 7
|
||||
@testcase | 110 | 5 | 5 10 100 100 100 -1
|
||||
@testcase | 110 | 0 | (null) (null) (null) (null) (null) 0
|
||||
*/
|
||||
|
|
28
tolk-tester/tests/warnings-1.tolk
Normal file
28
tolk-tester/tests/warnings-1.tolk
Normal file
|
@ -0,0 +1,28 @@
|
|||
fun getNullableInt(): int? { return null; }
|
||||
|
||||
fun main() {
|
||||
var c: int? = 6;
|
||||
__expect_type(c, "int");
|
||||
if (c == null) {}
|
||||
|
||||
var d: int? = c;
|
||||
if (((d)) != null && tupleSize(createEmptyTuple())) {}
|
||||
|
||||
var e: int? = getNullableInt();
|
||||
if (e != null) {
|
||||
return true;
|
||||
}
|
||||
__expect_type(e, "null");
|
||||
null == e;
|
||||
|
||||
return null != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | | 0
|
||||
|
||||
@stderr warning: variable `c` of type `int` is always not null, this condition is always false
|
||||
@stderr warning: variable `d` of type `int` is always not null, this condition is always true
|
||||
@stderr warning: variable `e` is always null, this condition is always true
|
||||
@stderr warning: expression is always null, this condition is always false
|
||||
*/
|
26
tolk-tester/tests/warnings-2.tolk
Normal file
26
tolk-tester/tests/warnings-2.tolk
Normal file
|
@ -0,0 +1,26 @@
|
|||
fun main() {
|
||||
var (a, b, c, d, e) = (1, beginCell(), beginCell().endCell().beginParse(), [1], true as bool?);
|
||||
|
||||
var alwaysInt = a != null ? 1 : null;
|
||||
__expect_type(alwaysInt, "int");
|
||||
|
||||
if (!(c == null)) {
|
||||
if (10 < 3) { assert(b == null, 100); }
|
||||
}
|
||||
while (d == null || false) {}
|
||||
|
||||
return e! != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@testcase | 0 | | -1
|
||||
|
||||
@stderr warning: variable `a` of type `int` is always not null, this condition is always true
|
||||
@stderr warning: condition of ternary operator is always true
|
||||
@stderr warning: variable `c` of type `slice` is always not null, this condition is always false
|
||||
@stderr warning: condition of `if` is always true
|
||||
@stderr warning: variable `b` of type `builder` is always not null, this condition is always false
|
||||
@stderr warning: condition of `assert` is always false
|
||||
@stderr warning: condition of `while` is always false
|
||||
@stderr warning: expression of type `bool` is always not null, this condition is always true
|
||||
*/
|
|
@ -12,8 +12,8 @@ set(TOLK_SOURCE
|
|||
pipe-register-symbols.cpp
|
||||
pipe-resolve-identifiers.cpp
|
||||
pipe-calc-rvalue-lvalue.cpp
|
||||
pipe-detect-unreachable.cpp
|
||||
pipe-infer-types-and-calls.cpp
|
||||
pipe-check-inferred-types.cpp
|
||||
pipe-refine-lvalue-for-mutate.cpp
|
||||
pipe-check-rvalue-lvalue.cpp
|
||||
pipe-check-pure-impure.cpp
|
||||
|
@ -23,6 +23,7 @@ set(TOLK_SOURCE
|
|||
pipe-find-unused-symbols.cpp
|
||||
pipe-generate-fif-output.cpp
|
||||
type-system.cpp
|
||||
smart-casts-cfg.cpp
|
||||
generics-helpers.cpp
|
||||
abscode.cpp
|
||||
analyzer.cpp
|
||||
|
|
|
@ -402,7 +402,7 @@ void CodeBlob::print(std::ostream& os, int flags) const {
|
|||
|
||||
std::vector<var_idx_t> CodeBlob::create_var(TypePtr var_type, SrcLocation loc, std::string name) {
|
||||
std::vector<var_idx_t> ir_idx;
|
||||
int stack_w = var_type->calc_width_on_stack();
|
||||
int stack_w = var_type->get_width_on_stack();
|
||||
ir_idx.reserve(stack_w);
|
||||
if (const TypeDataTensor* t_tensor = var_type->try_as<TypeDataTensor>()) {
|
||||
for (int i = 0; i < t_tensor->size(); ++i) {
|
||||
|
@ -410,7 +410,11 @@ std::vector<var_idx_t> CodeBlob::create_var(TypePtr var_type, SrcLocation loc, s
|
|||
std::vector<var_idx_t> nested = create_var(t_tensor->items[i], loc, std::move(sub_name));
|
||||
ir_idx.insert(ir_idx.end(), nested.begin(), nested.end());
|
||||
}
|
||||
} else if (var_type != TypeDataVoid::create()) {
|
||||
} else if (const TypeDataNullable* t_nullable = var_type->try_as<TypeDataNullable>(); t_nullable && stack_w != 1) {
|
||||
std::string null_flag_name = name.empty() ? name : name + ".NNFlag";
|
||||
ir_idx = create_var(t_nullable->inner, loc, std::move(name));
|
||||
ir_idx.emplace_back(create_var(TypeDataBool::create(), loc, std::move(null_flag_name))[0]);
|
||||
} else if (var_type != TypeDataVoid::create() && var_type != TypeDataNever::create()) {
|
||||
#ifdef TOLK_DEBUG
|
||||
tolk_assert(stack_w == 1);
|
||||
#endif
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue