Skip to content

Commit

Permalink
Merge pull request #110 from tonlabs/0.64.0
Browse files Browse the repository at this point in the history
0.64.0
  • Loading branch information
BorisI authored Aug 22, 2022
2 parents 5c829ec + e46ff38 commit 1fb6230
Show file tree
Hide file tree
Showing 40 changed files with 548 additions and 310 deletions.
62 changes: 61 additions & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ contract development.
* [\<optional(Type)\>.set()](#optionaltypeset)
* [\<optional(Type)\>.reset()](#optionaltypereset)
* [Keyword `null`](#keyword-null)
* [variant](#variant)
* [variant.isUint()](#variantisuint)
* [variant.toUint()](#varianttouint)
* [vector(Type)](#vectortype)
* [\<vector(Type)\>.push(Type)](#vectortypepushtype)
* [\<vector(Type)\>.pop()](#vectortypepop)
Expand All @@ -68,6 +71,7 @@ contract development.
* [TON specific control structures](#ton-specific-control-structures)
* [Range-based for loop](#range-based-for-loop)
* [repeat](#repeat)
* [try-catch](#trycatch)
* [Changes and extensions in Solidity types](#changes-and-extensions-in-solidity-types)
* [Integers](#integers)
* [bitSize() and uBitSize()](#bitsize-and-ubitsize)
Expand Down Expand Up @@ -893,7 +897,7 @@ Replaces content of the `optional` with **value**.
<optional(Type)>.reset();
```

Deletes contents of the `optional`.
Deletes content of the `optional`.

##### Keyword `null`

Expand All @@ -905,6 +909,22 @@ optional(uint) x = 123;
x = null; // reset value
```

#### variant

The `variant` type acts like a union for the most common solidity data types. Supported only `uint` so far.

#### variant.isUint()

```TVMSolidity
<variant>.isUint() returns (bool)
```

Checks whether `<variant>` holds `uint` type.

#### variant.toUint()

Converts `<variant>` to `uint` type if it's possible. Otherwise, throws an exception with code `77`.

#### vector(Type)

`vector(Type)` is a template container type capable of storing an arbitrary set of values of a
Expand Down Expand Up @@ -1053,6 +1073,45 @@ repeat(a - 1) {
// a == 1
```

#### try-catch

It is an experimental feature available only in certain blockchain networks.

The `try` statement allows you to define a block of code to be tested for errors while it is executed. The
`catch` statement allows you to define a block of code to be executed, if an error occurs in the try block.
`catch` block gets two parameters of type variant and uint, whick contain exception argument and code respectively.
Example:

```TVMSolidity
TvmBuilder builder;
uint c = 0;
try {
c = a + b;
require(c != 42, 100, 22);
require(c != 43, 100, 33);
builder.store(c);
} catch (variant value, uint errorCode) {
uint errorValue;
if (value.isUint()) {
errorValue = value.toUint();
}
if (errorCode == 100) {
if (errorValue == 22) {
// it was line: `require(c != 42, 100, 22);`
} else if (errorValue == 33) {
// it was line: `require(c != 43, 100, 33);`
}
} else if (errorCode == 8) {
// Cell overflow
// It was line: `builder.store(c);`
} else if (errorCode == 4) {
// Integer overflow
// It was line: `c = a + b;`
}
}
```

### Changes and extensions in Solidity types

#### Integers
Expand Down Expand Up @@ -4386,6 +4445,7 @@ Solidity runtime error codes:
* **74** - Await answer message has wrong source address.
* **75** - Await answer message has wrong function id.
* **76** - Public function was called before constructor.
* **77** - It's impossible to convert `variant` type to target type. See [variant.toUint()](#varianttouint)

### Division and rounding

Expand Down
12 changes: 12 additions & 0 deletions Changelog_TON.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
### 0.64.0 (2022-08-18)

Fixed build [sold](https://github.com/tonlabs/TON-Solidity-Compiler/tree/master/sold) for Windows and macOS.

Compiler features:
* Supported [ABI v2.3](https://github.com/tonlabs/ton-labs-abi/blob/master/docs/ABI_2.3_spec.md).
* Supported try-catch (experimental feature).
* Supported type `variant`.

Typo:
* Rename function `storeZeros` -> `storeZeroes`.

### 0.63.0 (2022-07-27)

Compiler features:
Expand Down
2 changes: 1 addition & 1 deletion compiler/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ include(EthPolicy)
eth_policy()

# project name and version should be set after cmake_policy CMP0048
set(PROJECT_VERSION "0.63.0")
set(PROJECT_VERSION "0.64.0")
project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX)

include(TestBigEndian)
Expand Down
1 change: 1 addition & 0 deletions compiler/liblangutil/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ namespace solidity::langutil
K(TvmSlice, "TvmSlice", 0) \
K(TvmBuilder, "TvmBuilder", 0) \
K(ExtraCurrencyCollection, "ExtraCurrencyCollection", 0) \
K(Variant, "variant", 0) \
K(Fixed, "fixed", 0) \
K(UFixed, "ufixed", 0) \
T(IntM, "intM", 0) \
Expand Down
7 changes: 3 additions & 4 deletions compiler/libsolidity/analysis/ControlFlowBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,10 @@ bool ControlFlowBuilder::visit(Conditional const& _conditional)

bool ControlFlowBuilder::visit(TryStatement const& _tryStatement)
{
appendControlFlow(_tryStatement.externalCall());
appendControlFlow(_tryStatement.body());

auto nodes = splitFlow(_tryStatement.clauses().size());
for (size_t i = 0; i < _tryStatement.clauses().size(); ++i)
nodes[i] = createFlow(nodes[i], _tryStatement.clauses()[i]->block());
auto nodes = splitFlow(1);
nodes[0] = createFlow(nodes[0], _tryStatement.clause().block());
mergeFlow(nodes);

return false;
Expand Down
146 changes: 18 additions & 128 deletions compiler/libsolidity/analysis/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1026,134 +1026,24 @@ bool TypeChecker::visit(IfStatement const& _ifStatement)

void TypeChecker::endVisit(TryStatement const& _tryStatement)
{
m_errorReporter.fatalTypeError(
_tryStatement.location(),
"Try-catch statement is not supported yet."
);

FunctionCall const* externalCall = dynamic_cast<FunctionCall const*>(&_tryStatement.externalCall());
if (!externalCall || externalCall->annotation().kind != FunctionCallKind::FunctionCall)
{
m_errorReporter.typeError(
_tryStatement.externalCall().location(),
"Try can only be used with external function calls and contract creation calls."
);
return;
}

FunctionType const& functionType = dynamic_cast<FunctionType const&>(*externalCall->expression().annotation().type);
if (
functionType.kind() != FunctionType::Kind::External &&
functionType.kind() != FunctionType::Kind::Creation &&
functionType.kind() != FunctionType::Kind::DelegateCall
)
{
m_errorReporter.typeError(
_tryStatement.externalCall().location(),
"Try can only be used with external function calls and contract creation calls."
);
return;
}

externalCall->annotation().tryCall = true;

solAssert(_tryStatement.clauses().size() >= 2, "");
solAssert(_tryStatement.clauses().front(), "");

TryCatchClause const& successClause = *_tryStatement.clauses().front();
if (successClause.parameters())
{
TypePointers returnTypes =
m_evmVersion.supportsReturndata() ?
functionType.returnParameterTypes() :
functionType.returnParameterTypesWithoutDynamicTypes();
std::vector<ASTPointer<VariableDeclaration>> const& parameters =
successClause.parameters()->parameters();
if (returnTypes.size() != parameters.size())
m_errorReporter.typeError(
successClause.location(),
"Function returns " +
to_string(functionType.returnParameterTypes().size()) +
" values, but returns clause has " +
to_string(parameters.size()) +
" variables."
);
size_t len = min(returnTypes.size(), parameters.size());
for (size_t i = 0; i < len; ++i)
{
solAssert(returnTypes[i], "");
if (parameters[i] && *parameters[i]->annotation().type != *returnTypes[i])
m_errorReporter.typeError(
parameters[i]->location(),
"Invalid type, expected " +
returnTypes[i]->toString(false) +
" but got " +
parameters[i]->annotation().type->toString() +
"."
);
}
}

TryCatchClause const* errorClause = nullptr;
TryCatchClause const* lowLevelClause = nullptr;
for (size_t i = 1; i < _tryStatement.clauses().size(); ++i)
{
TryCatchClause const& clause = *_tryStatement.clauses()[i];
if (clause.errorName() == "")
{
if (lowLevelClause)
m_errorReporter.typeError(
clause.location(),
SecondarySourceLocation{}.append("The first clause is here:", lowLevelClause->location()),
"This try statement already has a low-level catch clause."
);
lowLevelClause = &clause;
if (clause.parameters() && !clause.parameters()->parameters().empty())
{
if (
clause.parameters()->parameters().size() != 1 ||
*clause.parameters()->parameters().front()->type() != *TypeProvider::bytesMemory()
)
m_errorReporter.typeError(clause.location(), "Expected `catch (bytes memory ...) { ... }` or `catch { ... }`.");
if (!m_evmVersion.supportsReturndata())
m_errorReporter.typeError(
clause.location(),
"This catch clause type cannot be used on the selected EVM version (" +
m_evmVersion.name() +
"). You need at least a Byzantium-compatible EVM or use `catch { ... }`."
);
}
}
else if (clause.errorName() == "Error")
{
if (!m_evmVersion.supportsReturndata())
m_errorReporter.typeError(
clause.location(),
"This catch clause type cannot be used on the selected EVM version (" +
m_evmVersion.name() +
"). You need at least a Byzantium-compatible EVM or use `catch { ... }`."
);

if (errorClause)
m_errorReporter.typeError(
clause.location(),
SecondarySourceLocation{}.append("The first clause is here:", errorClause->location()),
"This try statement already has an \"Error\" catch clause."
);
errorClause = &clause;
if (
!clause.parameters() ||
clause.parameters()->parameters().size() != 1 ||
*clause.parameters()->parameters().front()->type() != *TypeProvider::stringMemory()
)
m_errorReporter.typeError(clause.location(), "Expected `catch Error(string memory ...) { ... }`.");
}
else
m_errorReporter.typeError(
clause.location(),
"Invalid catch clause name. Expected either `catch (...)` or `catch Error(...)`."
);
}
TryCatchClause const& clause = _tryStatement.clause();
std::vector<ASTPointer<VariableDeclaration>> const& errArgs = clause.parameters()->parameters();

auto printError = [&](SourceLocation const& loc){
m_errorReporter.typeError(
loc,
"Expected `catch (variant value, uint number) { ... }`.");
};
if (errArgs.size() != 2) {
printError(clause.location());
return;
}
if (*errArgs.at(0)->type() != *TypeProvider::variant()) {
printError(errArgs.at(0)->location());
}
if (*errArgs.at(1)->type() != *TypeProvider::uint256()) {
printError(errArgs.at(1)->location());
}
}

bool TypeChecker::visit(WhileStatement const& _whileStatement)
Expand Down
16 changes: 8 additions & 8 deletions compiler/libsolidity/ast/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -1431,22 +1431,22 @@ class TryStatement: public Statement
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _externalCall,
std::vector<ASTPointer<TryCatchClause>> const& _clauses
ASTPointer<Block> _body,
ASTPointer<TryCatchClause> _clause
):
Statement(_id, _location, _docString),
m_externalCall(_externalCall),
m_clauses(_clauses)
m_body(_body),
m_clause(_clause)
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;

Expression const& externalCall() const { return *m_externalCall; }
std::vector<ASTPointer<TryCatchClause>> const& clauses() const { return m_clauses; }
Block const& body() const { return *m_body; }
TryCatchClause const& clause() const { return *m_clause; }

private:
ASTPointer<Expression> m_externalCall;
std::vector<ASTPointer<TryCatchClause>> m_clauses;
ASTPointer<Block> m_body;
ASTPointer<TryCatchClause> m_clause;
};

/**
Expand Down
4 changes: 2 additions & 2 deletions compiler/libsolidity/ast/ASTJsonConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@ bool ASTJsonConverter::visit(TryCatchClause const& _node)
bool ASTJsonConverter::visit(TryStatement const& _node)
{
setJsonNode(_node, "TryStatement", {
make_pair("externalCall", toJson(_node.externalCall())),
make_pair("clauses", toJson(_node.clauses()))
make_pair("body", toJson(_node.body())),
make_pair("clause", toJson(_node.clause()))
});
return false;
}
Expand Down
10 changes: 4 additions & 6 deletions compiler/libsolidity/ast/ASTJsonImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,16 +578,14 @@ ASTPointer<TryCatchClause> ASTJsonImporter::createTryCatchClause(Json::Value con

ASTPointer<TryStatement> ASTJsonImporter::createTryStatement(Json::Value const& _node)
{
vector<ASTPointer<TryCatchClause>> clauses;

for (auto& param: _node["clauses"])
clauses.emplace_back(createTryCatchClause(param));
ASTPointer<Block> body = createBlock(_node["block"]);
ASTPointer<TryCatchClause> clause = createTryCatchClause(_node["clause"]);

return createASTNode<TryStatement>(
_node,
nullOrASTString(_node, "documentation"),
convertJsonToASTNode<Expression>(member(_node, "externalCall")),
clauses
body,
clause
);
}

Expand Down
8 changes: 4 additions & 4 deletions compiler/libsolidity/ast/AST_accept.h
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,8 @@ void TryStatement::accept(ASTVisitor& _visitor)
{
if (_visitor.visit(*this))
{
m_externalCall->accept(_visitor);
listAccept(m_clauses, _visitor);
m_body->accept(_visitor);
m_clause->accept(_visitor);
}
_visitor.endVisit(*this);
}
Expand All @@ -576,8 +576,8 @@ void TryStatement::accept(ASTConstVisitor& _visitor) const
{
if (_visitor.visit(*this))
{
m_externalCall->accept(_visitor);
listAccept(m_clauses, _visitor);
m_body->accept(_visitor);
m_clause->accept(_visitor);
}
_visitor.endVisit(*this);
}
Expand Down
Loading

0 comments on commit 1fb6230

Please sign in to comment.