Merge remote-tracking branch 'origin/develop' into develop_060

This commit is contained in:
chriseth 2019-11-14 13:42:46 +01:00
commit 216e1749f4
85 changed files with 1406 additions and 110 deletions

View File

@ -104,17 +104,6 @@ defaults:
name: command line tests
command: ./test/cmdlineTests.sh
- test_ubuntu1904: &test_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
steps:
- checkout
- attach_workspace:
at: build
- run: *run_soltest
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- test_ubuntu1904_clang: &test_ubuntu1904_clang
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904-clang
@ -126,7 +115,7 @@ defaults:
- store_test_results: *store_test_results
- store_artifacts: *artifacts_test_results
- test_ubuntu1904_all: &test_ubuntu1904
- test_ubuntu1904: &test_ubuntu1904
docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904
steps:

View File

@ -36,23 +36,29 @@ Compiler Features:
* Allow revert strings to be stripped from the binary using the ``--revert-strings`` option or the ``settings.debug.revertStrings`` setting.
### 0.5.13 (unreleased)
### 0.5.13 (2019-11-14)
Language Features:
* Allow to obtain the address of a linked library with ``address(LibraryName)``.
Compiler Features:
* Code Generator: Use SELFBALANCE for ``address(this).balance`` if using Istanbul EVM
* Code Generator: Use SELFBALANCE opcode for ``address(this).balance`` if using Istanbul EVM.
* EWasm: Experimental EWasm binary output via ``--ewasm`` and as documented in standard-json.
* SMTChecker: Add break/continue support to the CHC engine.
* SMTChecker: Support assignments to multi-dimensional arrays and mappings.
* SMTChecker: Support inheritance and function overriding.
* EWasm: Experimental EWasm binary output.
* Standard JSON Interface: Output the storage layout of a contract when artifact ``storageLayout`` is requested.
* TypeChecker: List possible candidates when overload resolution fails.
* TypeChecker: Disallow variables of library types.
Bugfixes:
* Type Checker: Disallow constructor of the same class to be used as modifier
* Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer
* Type Checker: Treat magic variables as unknown identifiers in inline assembly
* Code Generator: Fixed a faulty assert that would wrongly trigger for array sizes exceeding unsigned integer.
* SMTChecker: Fix internal error when accessing indices of fixed bytes.
* SMTChecker: Fix internal error when using function pointers as arguments.
* SMTChecker: Fix internal error when implicitly converting string literals to fixed bytes.
* Type Checker: Disallow constructor of the same class to be used as modifier.
* Type Checker: Treat magic variables as unknown identifiers in inline assembly.
### 0.5.12 (2019-10-01)

View File

@ -754,6 +754,10 @@
"bugs": [],
"released": "2019-10-01"
},
"0.5.13": {
"bugs": [],
"released": "2019-11-14"
},
"0.5.2": {
"bugs": [
"SignedArrayStorageCopy",

View File

@ -178,6 +178,9 @@ custom types without the overhead of external function calls:
}
}
It is possible to obtain the address of a library by converting
the library type to the ``address`` type, i.e. using ``address(LibraryName)``.
As the compiler cannot know where the library will be
deployed at, these addresses have to be filled into the
final bytecode by a linker

View File

@ -8,6 +8,8 @@ Miscellaneous
Layout of State Variables in Storage
************************************
.. _storage-inplace-encoding:
Statically-sized variables (everything except mapping and dynamically-sized array types) are laid out contiguously in storage starting from position ``0``. Multiple, contiguous items that need less than 32 bytes are packed into a single storage slot if possible, according to the following rules:
- The first item in a storage slot is stored lower-order aligned.
@ -49,6 +51,8 @@ The elements of structs and arrays are stored after each other, just as if they
Mappings and Dynamic Arrays
===========================
.. _storage-hashed-encoding:
Due to their unpredictable size, mapping and dynamically-sized array types use a Keccak-256 hash
computation to find the starting position of the value or the array data. These starting positions are always full stack slots.
@ -88,6 +92,267 @@ by checking if the lowest bit is set: short (not set) and long (set).
.. note::
Handling invalidly encoded slots is currently not supported but may be added in the future.
JSON Output
===========
.. _storage-layout-top-level:
The storage layout of a contract can be requested via the :ref:`standard JSON interface <compiler-api>`. The output is a JSON object containing two keys,
``storage`` and ``types``. The ``storage`` object is an array where each
element has the following form:
.. code::
{
"astId": 2,
"contract": "fileA:A",
"label": "x",
"offset": 0,
"slot": "0",
"type": "t_uint256"
}
where the example above is the storage layout of ``contract A { uint x; }`` from source unit ``fileA``
and
- ``astId`` is the id of the AST node of the state variable's declaration
- ``contract`` is the name of the contract including its path as prefix
- ``label`` is the name of the state variable
- ``offset`` is the offset in bytes within the storage slot according to the encoding
- ``slot`` is the storage slot where the state variable resides or starts. This
number may be very large and therefore its JSON value is represented as a
string.
- ``type`` is an identifier used as key to the variable's type information (described in the following)
The given ``type``, in this case ``t_uint256`` represents an element in
``types``, which has the form:
.. code::
{
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32",
}
where
- ``encoding`` how the data is encoded in storage, where the possible values are:
- ``inplace``: data is laid out contiguously in storage (see :ref:`above <storage-inplace-encoding>`).
- ``mapping``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
- ``dynamic_array``: Keccak-256 hash-based method (see :ref:`above <storage-hashed-encoding>`).
- ``bytes``: single slot or Keccak-256 hash-based depending on the data size (see :ref:`above <bytes-and-string>`).
- ``label`` is the canonical type name.
- ``numberOfBytes`` is the number of used bytes (as a decimal string). Note that if ``numberOfBytes > 32`` this means that more than one slot is used.
Some types have extra information besides the four above. Mappings contain
its ``key`` and ``value`` types (again referencing an entry in this mapping
of types), arrays have its ``base`` type, and structs list their ``members`` in
the same format as the top-level ``storage`` (see :ref:`above
<storage-layout-top-level>`).
.. note ::
The JSON output format of a contract's storage layout is still considered experimental
and is subject to change in non-breaking releases of Solidity.
The following example shows a contract and its storage layout, containing
value and reference types, types that are encoded packed, and nested types.
.. code::
pragma solidity >=0.4.0 <0.7.0;
contract A {
struct S {
uint128 a;
uint128 b;
uint[2] staticArray;
uint[] dynArray;
}
uint x;
uint y;
S s;
address addr;
mapping (uint => mapping (address => bool)) map;
uint[] array;
string s1;
bytes b1;
}
.. code::
"storageLayout": {
"storage": [
{
"astId": 14,
"contract": "fileA:A",
"label": "x",
"offset": 0,
"slot": "0",
"type": "t_uint256"
},
{
"astId": 16,
"contract": "fileA:A",
"label": "y",
"offset": 0,
"slot": "1",
"type": "t_uint256"
},
{
"astId": 18,
"contract": "fileA:A",
"label": "s",
"offset": 0,
"slot": "2",
"type": "t_struct(S)12_storage"
},
{
"astId": 20,
"contract": "fileA:A",
"label": "addr",
"offset": 0,
"slot": "6",
"type": "t_address"
},
{
"astId": 26,
"contract": "fileA:A",
"label": "map",
"offset": 0,
"slot": "7",
"type": "t_mapping(t_uint256,t_mapping(t_address,t_bool))"
},
{
"astId": 29,
"contract": "fileA:A",
"label": "array",
"offset": 0,
"slot": "8",
"type": "t_array(t_uint256)dyn_storage"
},
{
"astId": 31,
"contract": "fileA:A",
"label": "s1",
"offset": 0,
"slot": "9",
"type": "t_string_storage"
},
{
"astId": 33,
"contract": "fileA:A",
"label": "b1",
"offset": 0,
"slot": "10",
"type": "t_bytes_storage"
}
],
"types": {
"t_address": {
"encoding": "inplace",
"label": "address",
"numberOfBytes": "20"
},
"t_array(t_uint256)2_storage": {
"base": "t_uint256",
"encoding": "inplace",
"label": "uint256[2]",
"numberOfBytes": "64"
},
"t_array(t_uint256)dyn_storage": {
"base": "t_uint256",
"encoding": "dynamic_array",
"label": "uint256[]",
"numberOfBytes": "32"
},
"t_bool": {
"encoding": "inplace",
"label": "bool",
"numberOfBytes": "1"
},
"t_bytes_storage": {
"encoding": "bytes",
"label": "bytes",
"numberOfBytes": "32"
},
"t_mapping(t_address,t_bool)": {
"encoding": "mapping",
"key": "t_address",
"label": "mapping(address => bool)",
"numberOfBytes": "32",
"value": "t_bool"
},
"t_mapping(t_uint256,t_mapping(t_address,t_bool))": {
"encoding": "mapping",
"key": "t_uint256",
"label": "mapping(uint256 => mapping(address => bool))",
"numberOfBytes": "32",
"value": "t_mapping(t_address,t_bool)"
},
"t_string_storage": {
"encoding": "bytes",
"label": "string",
"numberOfBytes": "32"
},
"t_struct(S)12_storage": {
"encoding": "inplace",
"label": "struct A.S",
"members": [
{
"astId": 2,
"contract": "fileA:A",
"label": "a",
"offset": 0,
"slot": "0",
"type": "t_uint128"
},
{
"astId": 4,
"contract": "fileA:A",
"label": "b",
"offset": 16,
"slot": "0",
"type": "t_uint128"
},
{
"astId": 8,
"contract": "fileA:A",
"label": "staticArray",
"offset": 0,
"slot": "1",
"type": "t_array(t_uint256)2_storage"
},
{
"astId": 11,
"contract": "fileA:A",
"label": "dynArray",
"offset": 0,
"slot": "3",
"type": "t_array(t_uint256)dyn_storage"
}
],
"numberOfBytes": "128"
},
"t_uint128": {
"encoding": "inplace",
"label": "uint128",
"numberOfBytes": "16"
},
"t_uint256": {
"encoding": "inplace",
"label": "uint256",
"numberOfBytes": "32"
}
}
}
.. index: memory layout
****************

View File

@ -291,6 +291,7 @@ Input Description
// metadata - Metadata
// ir - Yul intermediate representation of the code before optimization
// irOptimized - Intermediate representation after optimization
// storageLayout - Slots, offsets and types of the contract's state variables.
// evm.assembly - New assembly format
// evm.legacyAssembly - Old-style assembly format in JSON
// evm.bytecode.object - Bytecode object
@ -391,6 +392,8 @@ Output Description
"devdoc": {},
// Intermediate representation (string)
"ir": "",
// See the Storage Layout documentation.
"storageLayout": {"storage": [...], "types": {...} },
// EVM-related outputs
"evm": {
// Assembly (string)

View File

@ -112,6 +112,8 @@ set(sources
interface/ReadFile.h
interface/StandardCompiler.cpp
interface/StandardCompiler.h
interface/StorageLayout.cpp
interface/StorageLayout.h
interface/Version.cpp
interface/Version.h
parsing/DocStringParser.cpp

View File

@ -38,10 +38,12 @@ namespace solidity
NameAndTypeResolver::NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ErrorReporter& _errorReporter
) :
):
m_scopes(_scopes),
m_evmVersion(_evmVersion),
m_errorReporter(_errorReporter),
m_globalContext(_globalContext)
{
@ -324,7 +326,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
{
if (m_scopes.count(&_node))
setScope(&_node);
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
return ReferencesResolver(m_errorReporter, *this, m_evmVersion, _resolveInsideCode).resolve(_node);
}
}

View File

@ -28,6 +28,8 @@
#include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTVisitor.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp>
#include <list>
@ -55,6 +57,7 @@ public:
/// are filled during the lifetime of this object.
NameAndTypeResolver(
GlobalContext& _globalContext,
langutil::EVMVersion _evmVersion,
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
langutil::ErrorReporter& _errorReporter
);
@ -130,6 +133,7 @@ private:
/// Aliases (for example `import "x" as y;`) create multiple pointers to the same scope.
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
langutil::EVMVersion m_evmVersion;
DeclarationContainer* m_currentScope = nullptr;
langutil::ErrorReporter& m_errorReporter;
GlobalContext& m_globalContext;

View File

@ -350,7 +350,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
yul::AsmAnalyzer(
analysisInfo,
errorsIgnored,
yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}),
yul::EVMDialect::strictAssemblyForEVM(m_evmVersion),
resolver
).analyze(_inlineAssembly.operations());
return false;

View File

@ -24,6 +24,7 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp>
#include <list>
@ -52,10 +53,12 @@ public:
ReferencesResolver(
langutil::ErrorReporter& _errorReporter,
NameAndTypeResolver& _resolver,
langutil::EVMVersion _evmVersion,
bool _resolveInsideCode = false
):
m_errorReporter(_errorReporter),
m_resolver(_resolver),
m_evmVersion(_evmVersion),
m_resolveInsideCode(_resolveInsideCode)
{}
@ -99,6 +102,7 @@ private:
langutil::ErrorReporter& m_errorReporter;
NameAndTypeResolver& m_resolver;
langutil::EVMVersion m_evmVersion;
/// Stack of return parameters.
std::vector<ParameterList const*> m_returnParameters;
bool const m_resolveInsideCode;

View File

@ -457,6 +457,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
TypePointer varType = _variable.annotation().type;
solAssert(!!varType, "Variable type not provided.");
if (auto contractType = dynamic_cast<ContractType const*>(varType))
if (contractType->contractDefinition().isLibrary())
m_errorReporter.typeError(_variable.location(), "The type of a variable cannot be a library.");
if (_variable.value())
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
@ -2633,12 +2636,30 @@ bool TypeChecker::visit(Identifier const& _identifier)
if (functionType->canTakeArguments(*annotation.arguments))
candidates.push_back(declaration);
}
if (candidates.empty())
m_errorReporter.fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
else if (candidates.size() == 1)
if (candidates.size() == 1)
annotation.referencedDeclaration = candidates.front();
else
m_errorReporter.fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
{
SecondarySourceLocation ssl;
for (Declaration const* declaration: annotation.overloadedDeclarations)
if (declaration->location().isEmpty())
{
// Try to re-construct function definition
string description;
for (auto const& param: declaration->functionType(true)->parameterTypes())
description += (description.empty() ? "" : ", ") + param->toString(false);
description = "function " + _identifier.name() + "(" + description + ")";
ssl.append("Candidate: " + description, declaration->location());
}
else
ssl.append("Candidate:", declaration->location());
if (candidates.empty())
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No matching declaration found after argument-dependent lookup.");
else
m_errorReporter.fatalTypeError(_identifier.location(), ssl, "No unique declaration found after argument-dependent lookup.");
}
}
}
solAssert(

View File

@ -3411,6 +3411,15 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current
return members;
}
BoolResult TypeType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
if (auto const* address = dynamic_cast<AddressType const*>(&_convertTo))
if (address->stateMutability() == StateMutability::NonPayable)
if (auto const* contractType = dynamic_cast<ContractType const*>(m_actualType))
return contractType->contractDefinition().isLibrary();
return isImplicitlyConvertibleTo(_convertTo);
}
ModifierType::ModifierType(ModifierDefinition const& _modifier)
{
TypePointers params;

View File

@ -1317,6 +1317,7 @@ public:
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
private:
TypePointer m_actualType;
};

View File

@ -475,7 +475,22 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
solAssert(_functionCall.arguments().size() == 1, "");
solAssert(_functionCall.names().empty(), "");
acceptAndConvert(*_functionCall.arguments().front(), *_functionCall.annotation().type);
auto const& expression = *_functionCall.arguments().front();
auto const& targetType = *_functionCall.annotation().type;
if (auto const* typeType = dynamic_cast<TypeType const*>(expression.annotation().type))
if (auto const* addressType = dynamic_cast<AddressType const*>(&targetType))
{
auto const* contractType = dynamic_cast<ContractType const*>(typeType->actualType());
solAssert(
contractType &&
contractType->contractDefinition().isLibrary() &&
addressType->stateMutability() == StateMutability::NonPayable,
""
);
m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName());
return false;
}
acceptAndConvert(expression, targetType);
return false;
}

View File

@ -428,15 +428,20 @@ void BMC::inlineFunctionCall(FunctionCall const& _funCall)
auto const& funType = dynamic_cast<FunctionType const*>(calledExpr->annotation().type);
solAssert(funType, "");
auto const& functionParams = funDef->parameters();
auto const& arguments = _funCall.arguments();
unsigned firstParam = 0;
if (funType->bound())
{
auto const& boundFunction = dynamic_cast<MemberAccess const*>(calledExpr);
solAssert(boundFunction, "");
funArgs.push_back(expr(boundFunction->expression()));
funArgs.push_back(expr(boundFunction->expression(), functionParams.front()->type()));
firstParam = 1;
}
for (auto arg: _funCall.arguments())
funArgs.push_back(expr(*arg));
solAssert((arguments.size() + firstParam) == functionParams.size(), "");
for (unsigned i = 0; i < arguments.size(); ++i)
funArgs.push_back(expr(*arguments.at(i), functionParams.at(i + firstParam)->type()));
initializeFunctionCallParameters(*funDef, funArgs);
// The reason why we need to pushCallStack here instead of visit(FunctionDefinition)

View File

@ -508,7 +508,13 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts;
for (auto const& var: _function.parameters() + _function.returnParameters())
{
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>(
m_stateSorts + varSorts,
boolSort
@ -526,7 +532,13 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
auto boolSort = make_shared<smt::Sort>(smt::Kind::Bool);
vector<smt::SortPointer> varSorts;
for (auto const& var: m_currentFunction->localVariables())
{
// SMT solvers do not support function types as arguments.
if (var->type()->category() == Type::Category::Function)
varSorts.push_back(make_shared<smt::Sort>(smt::Kind::Int));
else
varSorts.push_back(smt::smtSort(*var->type()));
}
return make_shared<smt::FunctionSort>(
fSort->domain + varSorts,
boolSort
@ -540,7 +552,7 @@ unique_ptr<smt::SymbolicFunctionVariable> CHC::createSymbolicBlock(smt::SortPoin
_name,
m_context
);
m_interface->registerRelation(block->currentValue());
m_interface->registerRelation(block->currentFunctionValue());
return block;
}
@ -572,7 +584,7 @@ smt::Expression CHC::error()
smt::Expression CHC::error(unsigned _idx)
{
return m_errorPredicate->valueAtIndex(_idx)({});
return m_errorPredicate->functionValueAtIndex(_idx)({});
}
unique_ptr<smt::SymbolicFunctionVariable> CHC::createBlock(ASTNode const* _node, string const& _prefix)
@ -589,7 +601,7 @@ void CHC::createErrorBlock()
{
solAssert(m_errorPredicate, "");
m_errorPredicate->increaseIndex();
m_interface->registerRelation(m_errorPredicate->currentValue());
m_interface->registerRelation(m_errorPredicate->currentFunctionValue());
}
void CHC::connectBlocks(smt::Expression const& _from, smt::Expression const& _to, smt::Expression const& _constraints)

View File

@ -154,15 +154,15 @@ private:
//@{
/// Constructor predicate.
/// Default constructor sets state vars to 0.
std::unique_ptr<smt::SymbolicVariable> m_constructorPredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_constructorPredicate;
/// Artificial Interface predicate.
/// Single entry block for all functions.
std::unique_ptr<smt::SymbolicVariable> m_interfacePredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_interfacePredicate;
/// Artificial Error predicate.
/// Single error block for all assertions.
std::unique_ptr<smt::SymbolicVariable> m_errorPredicate;
std::unique_ptr<smt::SymbolicFunctionVariable> m_errorPredicate;
//@}
/// Variables.

View File

@ -136,9 +136,13 @@ void SMTEncoder::visitFunctionOrModifier()
*modifierInvocation->name()->annotation().referencedDeclaration
);
vector<smt::Expression> modifierArgsExpr;
if (modifierInvocation->arguments())
for (auto arg: *modifierInvocation->arguments())
modifierArgsExpr.push_back(expr(*arg));
if (auto const* arguments = modifierInvocation->arguments())
{
auto const& modifierParams = modifierDef.parameters();
solAssert(modifierParams.size() == arguments->size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
modifierArgsExpr.push_back(expr(*arguments->at(i), modifierParams.at(i)->type()));
}
initializeFunctionCallParameters(modifierDef, modifierArgsExpr);
pushCallStack({&modifierDef, modifierInvocation.get()});
modifierDef.body().accept(*this);
@ -595,10 +599,10 @@ void SMTEncoder::endVisit(Identifier const& _identifier)
{
// Will be translated as part of the node that requested the lvalue.
}
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (auto decl = identifierToVariable(_identifier))
defineExpr(_identifier, currentValue(*decl));
else if (_identifier.annotation().type->category() == Type::Category::Function)
visitFunctionIdentifier(_identifier);
else if (_identifier.name() == "now")
defineGlobalVariable(_identifier.name(), _identifier);
else if (_identifier.name() == "this")
@ -699,7 +703,7 @@ void SMTEncoder::endVisit(Return const& _return)
}
}
else if (returnParams.size() == 1)
m_context.addAssertion(expr(*_return.expression()) == m_context.newValue(*returnParams.front()));
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
}
}
@ -1353,7 +1357,7 @@ void SMTEncoder::createExpr(Expression const& _e)
void SMTEncoder::defineExpr(Expression const& _e, smt::Expression _value)
{
createExpr(_e);
solAssert(smt::smtKind(_e.annotation().type->category()) != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
solAssert(_value.sort->kind != smt::Kind::Function, "Equality operator applied to type that is not fully supported");
m_context.addAssertion(expr(_e) == _value);
}

View File

@ -19,6 +19,7 @@
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/ast/Types.h>
#include <libdevcore/CommonData.h>
#include <memory>
using namespace std;
@ -139,7 +140,28 @@ pair<bool, shared_ptr<SymbolicVariable>> newSymbolicVariable(
else if (isBool(_type.category()))
var = make_shared<SymbolicBoolVariable>(type, _uniqueName, _context);
else if (isFunction(_type.category()))
{
auto const& fType = dynamic_cast<FunctionType const*>(type);
auto const& paramsIn = fType->parameterTypes();
auto const& paramsOut = fType->returnParameterTypes();
auto findFunctionParam = [&](auto&& params) {
return find_if(
begin(params),
end(params),
[&](TypePointer _paramType) { return _paramType->category() == solidity::Type::Category::Function; }
);
};
if (
findFunctionParam(paramsIn) != end(paramsIn) ||
findFunctionParam(paramsOut) != end(paramsOut)
)
{
abstract = true;
var = make_shared<SymbolicIntVariable>(TypeProvider::uint256(), type, _uniqueName, _context);
}
else
var = make_shared<SymbolicFunctionVariable>(type, _uniqueName, _context);
}
else if (isInteger(_type.category()))
var = make_shared<SymbolicIntVariable>(type, type, _uniqueName, _context);
else if (isFixedBytes(_type.category()))

View File

@ -19,7 +19,6 @@
#include <libsolidity/formal/SymbolicTypes.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
using namespace std;
using namespace dev;
@ -153,16 +152,38 @@ SymbolicFunctionVariable::SymbolicFunctionVariable(
solAssert(m_sort->kind == Kind::Function, "");
}
void SymbolicFunctionVariable::resetDeclaration()
Expression SymbolicFunctionVariable::currentValue(solidity::TypePointer const& _targetType) const
{
m_declaration = m_context.newVariable(currentName(), m_sort);
return m_abstract.currentValue(_targetType);
}
Expression SymbolicFunctionVariable::currentFunctionValue() const
{
return m_declaration;
}
Expression SymbolicFunctionVariable::valueAtIndex(int _index) const
{
return m_abstract.valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::functionValueAtIndex(int _index) const
{
return SymbolicVariable::valueAtIndex(_index);
}
Expression SymbolicFunctionVariable::resetIndex()
{
SymbolicVariable::resetIndex();
return m_abstract.resetIndex();
}
Expression SymbolicFunctionVariable::increaseIndex()
{
++(*m_ssa);
resetDeclaration();
return currentValue();
m_abstract.increaseIndex();
return m_abstract.currentValue();
}
Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) const
@ -170,6 +191,11 @@ Expression SymbolicFunctionVariable::operator()(vector<Expression> _arguments) c
return m_declaration(_arguments);
}
void SymbolicFunctionVariable::resetDeclaration()
{
m_declaration = m_context.newVariable(currentName(), m_sort);
}
SymbolicMappingVariable::SymbolicMappingVariable(
solidity::TypePointer _type,
string _uniqueName,

View File

@ -20,6 +20,7 @@
#include <libsolidity/formal/SolverInterface.h>
#include <libsolidity/formal/SSAVariable.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/TypeProvider.h>
#include <memory>
namespace dev
@ -138,7 +139,12 @@ public:
};
/**
* Specialization of SymbolicVariable for FunctionType
* Specialization of SymbolicVariable for FunctionType.
* Besides containing a symbolic function declaration,
* it also has an integer used as abstraction.
* By default, the abstract representation is used when
* values are requested, and the function declaration is
* used when operator() is applied over arguments.
*/
class SymbolicFunctionVariable: public SymbolicVariable
{
@ -154,8 +160,20 @@ public:
EncodingContext& _context
);
Expression increaseIndex();
Expression operator()(std::vector<Expression> _arguments) const;
Expression currentValue(solidity::TypePointer const& _targetType = TypePointer{}) const override;
// Explicit request the function declaration.
Expression currentFunctionValue() const;
Expression valueAtIndex(int _index) const override;
// Explicit request the function declaration.
Expression functionValueAtIndex(int _index) const;
Expression resetIndex() override;
Expression increaseIndex() override;
Expression operator()(std::vector<Expression> _arguments) const override;
private:
/// Creates a new function declaration.
@ -163,6 +181,14 @@ private:
/// Stores the current function declaration.
Expression m_declaration;
/// Abstract representation.
SymbolicIntVariable m_abstract{
TypeProvider::uint256(),
TypeProvider::uint256(),
m_uniqueName + "_abstract",
m_context
};
};
/**

View File

@ -43,6 +43,7 @@
#include <libsolidity/interface/ABI.h>
#include <libsolidity/interface/Natspec.h>
#include <libsolidity/interface/GasEstimator.h>
#include <libsolidity/interface/StorageLayout.h>
#include <libsolidity/interface/Version.h>
#include <libsolidity/parsing/Parser.h>
@ -286,7 +287,7 @@ bool CompilerStack::analyze()
noErrors = false;
m_globalContext = make_shared<GlobalContext>();
NameAndTypeResolver resolver(*m_globalContext, m_scopes, m_errorReporter);
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_scopes, m_errorReporter);
for (Source const* source: m_sourceOrder)
if (!resolver.registerDeclarations(*source->ast))
return false;
@ -684,6 +685,28 @@ Json::Value const& CompilerStack::contractABI(Contract const& _contract) const
return *_contract.abi;
}
Json::Value const& CompilerStack::storageLayout(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
return storageLayout(contract(_contractName));
}
Json::Value const& CompilerStack::storageLayout(Contract const& _contract) const
{
if (m_stackState < AnalysisPerformed)
BOOST_THROW_EXCEPTION(CompilerError() << errinfo_comment("Analysis was not successful."));
solAssert(_contract.contract, "");
// caches the result
if (!_contract.storageLayout)
_contract.storageLayout.reset(new Json::Value(StorageLayout().generate(*_contract.contract)));
return *_contract.storageLayout;
}
Json::Value const& CompilerStack::natspecUser(string const& _contractName) const
{
if (m_stackState < AnalysisPerformed)

View File

@ -287,6 +287,10 @@ public:
/// Prerequisite: Successful call to parse or compile.
Json::Value const& contractABI(std::string const& _contractName) const;
/// @returns a JSON representing the storage layout of the contract.
/// Prerequisite: Successful call to parse or compile.
Json::Value const& storageLayout(std::string const& _contractName) const;
/// @returns a JSON representing the contract's user documentation.
/// Prerequisite: Successful call to parse or compile.
Json::Value const& natspecUser(std::string const& _contractName) const;
@ -334,6 +338,7 @@ private:
eth::LinkerObject eWasmObject; ///< Experimental eWasm code
mutable std::unique_ptr<std::string const> metadata; ///< The metadata json that will be hashed into the chain.
mutable std::unique_ptr<Json::Value const> abi;
mutable std::unique_ptr<Json::Value const> storageLayout;
mutable std::unique_ptr<Json::Value const> userDocumentation;
mutable std::unique_ptr<Json::Value const> devDocumentation;
mutable std::unique_ptr<std::string const> sourceMapping;
@ -397,6 +402,10 @@ private:
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& contractABI(Contract const&) const;
/// @returns the storage layout of the contract as a JSON object.
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& storageLayout(Contract const&) const;
/// @returns the Natspec User documentation as a JSON object.
/// This will generate the JSON object and store it in the Contract object if it is not present yet.
Json::Value const& natspecUser(Contract const&) const;

View File

@ -927,10 +927,12 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting
string file = contractName.substr(0, colon);
string name = contractName.substr(colon + 1);
// ABI, documentation and metadata
// ABI, storage layout, documentation and metadata
Json::Value contractData(Json::objectValue);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "abi", wildcardMatchesExperimental))
contractData["abi"] = compilerStack.contractABI(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "storageLayout", false))
contractData["storageLayout"] = compilerStack.storageLayout(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "metadata", wildcardMatchesExperimental))
contractData["metadata"] = compilerStack.metadata(contractName);
if (isArtifactRequested(_inputsAndSettings.outputSelection, file, name, "userdoc", wildcardMatchesExperimental))

View File

@ -0,0 +1,119 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
#include <libsolidity/interface/StorageLayout.h>
#include <libsolidity/ast/TypeProvider.h>
using namespace std;
using namespace dev;
using namespace dev::solidity;
Json::Value StorageLayout::generate(ContractDefinition const& _contractDef)
{
solAssert(!m_contract, "");
m_contract = &_contractDef;
m_types.clear();
auto typeType = dynamic_cast<TypeType const*>(_contractDef.type());
solAssert(typeType, "");
auto contractType = dynamic_cast<ContractType const*>(typeType->actualType());
solAssert(contractType, "");
Json::Value variables(Json::arrayValue);
for (auto [var, slot, offset]: contractType->stateVariables())
variables.append(generate(*var, slot, offset));
Json::Value layout;
layout["storage"] = move(variables);
layout["types"] = move(m_types);
return layout;
}
Json::Value StorageLayout::generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset)
{
Json::Value varEntry;
TypePointer varType = _var.type();
varEntry["label"] = _var.name();
varEntry["astId"] = int(_var.id());
varEntry["contract"] = m_contract->fullyQualifiedName();
varEntry["slot"] = _slot.str();
varEntry["offset"] = _offset;
varEntry["type"] = typeKeyName(varType);
generate(varType);
return varEntry;
}
void StorageLayout::generate(TypePointer _type)
{
if (m_types.isMember(typeKeyName(_type)))
return;
// Register it now to cut recursive visits.
Json::Value& typeInfo = m_types[typeKeyName(_type)];
typeInfo["label"] = _type->toString(true);
typeInfo["numberOfBytes"] = u256(_type->storageBytes() * _type->storageSize()).str();
if (auto structType = dynamic_cast<StructType const*>(_type))
{
Json::Value members(Json::arrayValue);
auto const& structDef = structType->structDefinition();
for (auto const& member: structDef.members())
{
auto const& offsets = structType->storageOffsetsOfMember(member->name());
members.append(generate(*member, offsets.first, offsets.second));
}
typeInfo["members"] = move(members);
typeInfo["encoding"] = "inplace";
}
else if (auto mappingType = dynamic_cast<MappingType const*>(_type))
{
typeInfo["key"] = typeKeyName(mappingType->keyType());
typeInfo["value"] = typeKeyName(mappingType->valueType());
generate(mappingType->keyType());
generate(mappingType->valueType());
typeInfo["encoding"] = "mapping";
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(_type))
{
if (arrayType->isByteArray())
typeInfo["encoding"] = "bytes";
else
{
typeInfo["base"] = typeKeyName(arrayType->baseType());
generate(arrayType->baseType());
typeInfo["encoding"] = arrayType->isDynamicallySized() ? "dynamic_array" : "inplace";
}
}
else
{
solAssert(_type->isValueType(), "");
typeInfo["encoding"] = "inplace";
}
solAssert(typeInfo.isMember("encoding"), "");
}
string StorageLayout::typeKeyName(TypePointer _type)
{
if (auto refType = dynamic_cast<ReferenceType const*>(_type))
return TypeProvider::withLocationIfReference(refType->location(), _type)->richIdentifier();
return _type->richIdentifier();
}

View File

@ -0,0 +1,58 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* Generates the storage layout of a contract.
*/
#pragma once
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
#include <json/json.h>
namespace dev
{
namespace solidity
{
class StorageLayout
{
public:
/// Generates the storage layout of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's storage layout.
Json::Value generate(ContractDefinition const& _contractDef);
private:
/// Generates the JSON information for a variable and its storage location.
Json::Value generate(VariableDeclaration const& _var, u256 const& _slot, unsigned _offset);
/// Generates the JSON information for @param _type
void generate(TypePointer _type);
/// The key for the JSON object describing a type.
std::string typeKeyName(TypePointer _type);
Json::Value m_types;
/// Current analyzed contract
ContractDefinition const* m_contract = nullptr;
};
}
}

View File

@ -0,0 +1,5 @@
require_overload/input.sol:4:9: Error: No matching declaration found after argument-dependent lookup.
require(this);
^-----^
Candidate: function require(bool)
Candidate: function require(bool, string memory)

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,6 @@
pragma solidity >=0.0;
contract C {
function f() public pure {
require(this);
}
}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { bytes s1 = \"test\"; bytes s2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_bytes_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_bytes_storage"}],"types":{"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { bytes s1 = \"test\"; bytes s2; }
^-----------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":43,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint[] array1; bool[] array2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"array1","offset":0,"slot":"0","type":"t_array(t_uint256)dyn_storage"},{"astId":6,"contract":"fileA:A","label":"array2","offset":0,"slot":"1","type":"t_array(t_bool)dyn_storage"}],"types":{"t_array(t_bool)dyn_storage":{"base":"t_bool","encoding":"dynamic_array","label":"bool[]","numberOfBytes":"32"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint[] array1; bool[] array2; }
^------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":44,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; mapping (uint => mapping (address => bool)) map; uint[] array; string s1; bytes b1; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"},{"astId":26,"contract":"fileA:A","label":"map","offset":0,"slot":"7","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"},{"astId":29,"contract":"fileA:A","label":"array","offset":0,"slot":"8","type":"t_array(t_uint256)dyn_storage"},{"astId":31,"contract":"fileA:A","label":"s1","offset":0,"slot":"9","type":"t_string_storage"},{"astId":33,"contract":"fileA:A","label":"b1","offset":0,"slot":"10","type":"t_bytes_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_bytes_storage":{"encoding":"bytes","label":"bytes","numberOfBytes":"32"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint128 a; ... int[] array; string s1; bytes b1; }
^-------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":206,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":10,"contract":"fileA:A","label":"map","offset":0,"slot":"2","type":"t_mapping(t_uint256,t_mapping(t_address,t_bool))"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_bool":{"encoding":"inplace","label":"bool","numberOfBytes":"1"},"t_mapping(t_address,t_bool)":{"encoding":"mapping","key":"t_address","label":"mapping(address => bool)","numberOfBytes":"32","value":"t_bool"},"t_mapping(t_uint256,t_mapping(t_address,t_bool))":{"encoding":"mapping","key":"t_uint256","label":"mapping(uint256 => mapping(address => bool))","numberOfBytes":"32","value":"t_mapping(t_address,t_bool)"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint x; uint y; mapping (uint => mapping (address => bool)) map; }
^-----------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":79,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { }
^------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,19 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { }"
},
"fileB": {
"content": "contract A { uint x; uint y; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[],"types":null}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { }
^------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":14,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0},"fileB":{"id":1}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { string s1 = \"test\"; string s2; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":3,"contract":"fileA:A","label":"s1","offset":0,"slot":"0","type":"t_string_storage"},{"astId":5,"contract":"fileA:A","label":"s2","offset":0,"slot":"1","type":"t_string_storage"}],"types":{"t_string_storage":{"encoding":"bytes","label":"string","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { string s1 = \"test\"; string s2; }
^-------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":45,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"7","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"b","offset":0,"slot":"1","type":"t_uint256"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"2","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"4","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"160"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint a; uint b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }
^------------------------------------------------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":116,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":14,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":16,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":18,"contract":"fileA:A","label":"s","offset":0,"slot":"2","type":"t_struct(S)12_storage"},{"astId":20,"contract":"fileA:A","label":"addr","offset":0,"slot":"6","type":"t_address"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_array(t_uint256)dyn_storage":{"base":"t_uint256","encoding":"dynamic_array","label":"uint256[]","numberOfBytes":"32"},"t_struct(S)12_storage":{"encoding":"inplace","label":"struct A.S","members":[{"astId":2,"contract":"fileA:A","label":"a","offset":0,"slot":"0","type":"t_uint128"},{"astId":4,"contract":"fileA:A","label":"b","offset":16,"slot":"0","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"staticArray","offset":0,"slot":"1","type":"t_array(t_uint256)2_storage"},{"astId":11,"contract":"fileA:A","label":"dynArray","offset":0,"slot":"3","type":"t_array(t_uint256)dyn_storage"}],"numberOfBytes":"128"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { struct S { uint128 a; uint128 b; uint[2] staticArray; uint[] dynArray; } uint x; uint y; S s; address addr; }
^------------------------------------------------------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":122,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint x; uint y; address addr; uint[2] array; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint256"},{"astId":4,"contract":"fileA:A","label":"y","offset":0,"slot":"1","type":"t_uint256"},{"astId":6,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":10,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint x; uint y; address addr; uint[2] array; }
^---------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":59,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -0,0 +1,16 @@
{
"language": "Solidity",
"sources": {
"fileA": {
"content": "contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }"
}
},
"settings": {
"outputSelection": {
"fileA": {
"A": [ "storageLayout" ],
"": [ "storageLayout" ]
}
}
}
}

View File

@ -0,0 +1,4 @@
{"contracts":{"fileA":{"A":{"storageLayout":{"storage":[{"astId":2,"contract":"fileA:A","label":"x","offset":0,"slot":"0","type":"t_uint64"},{"astId":4,"contract":"fileA:A","label":"y","offset":8,"slot":"0","type":"t_uint128"},{"astId":6,"contract":"fileA:A","label":"z","offset":0,"slot":"1","type":"t_uint128"},{"astId":8,"contract":"fileA:A","label":"addr","offset":0,"slot":"2","type":"t_address"},{"astId":12,"contract":"fileA:A","label":"array","offset":0,"slot":"3","type":"t_array(t_uint256)2_storage"}],"types":{"t_address":{"encoding":"inplace","label":"address","numberOfBytes":"20"},"t_array(t_uint256)2_storage":{"base":"t_uint256","encoding":"inplace","label":"uint256[2]","numberOfBytes":"64"},"t_uint128":{"encoding":"inplace","label":"uint128","numberOfBytes":"16"},"t_uint256":{"encoding":"inplace","label":"uint256","numberOfBytes":"32"},"t_uint64":{"encoding":"inplace","label":"uint64","numberOfBytes":"8"}}}}}},"errors":[{"component":"general","formattedMessage":"fileA:1:1: Warning: Source file does not specify required compiler version!
contract A { uint64 x; uint128 y; uint128 z; address addr; uint[2] array; }
^-------------------------------------------------------------------------^
","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":75,"file":"fileA","start":0},"type":"Warning"}],"sources":{"fileA":{"id":0}}}

View File

@ -1,9 +1,14 @@
contract X {}
library test {
struct StructType { uint a; }
function f(StructType storage b, uint[] storage c, test d) public returns (uint[] memory e, StructType storage f) { f = f; }
function f1(uint[] memory c, test d) public pure returns (uint[] memory e) { }
function f(StructType storage b, uint[] storage c, X d) public returns (uint[] memory e, StructType storage f) { f = f; }
function f1(uint[] memory c, X d) public pure returns (uint[] memory e) { }
}
// ----
// :X
// []
//
//
// :test
// [
// {
@ -15,9 +20,9 @@ library test {
// "type": "uint256[]"
// },
// {
// "internalType": "library test",
// "internalType": "contract X",
// "name": "d",
// "type": "test"
// "type": "X"
// }
// ],
// "name": "f1",

View File

@ -64,7 +64,7 @@ eth::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
GlobalContext globalContext;
NameAndTypeResolver resolver(globalContext, scopes, errorReporter);
NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter);
solAssert(Error::containsOnlyWarnings(errorReporter.errors()), "");
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())

View File

@ -78,15 +78,34 @@ TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePref
for (auto& test: m_tests)
test.reset();
map<string, dev::test::Address> libraries;
bool constructed = false;
for (auto& test: m_tests)
{
if (&test == &m_tests.front())
if (constructed)
{
soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments.");
}
else if (test.call().isLibrary)
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature
);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else
{
if (test.call().isConstructor)
deploy("", test.call().value, test.call().arguments.rawBytes());
deploy("", test.call().value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes()), "Failed to deploy contract.");
else
soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call.");
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor)
{
@ -171,8 +190,8 @@ void SemanticTest::parseExpectations(istream& _stream)
std::move(functionCalls.begin(), functionCalls.end(), back_inserter(m_tests));
}
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments)
bool SemanticTest::deploy(string const& _contractName, u256 const& _value, bytes const& _arguments, map<string, dev::test::Address> const& _libraries)
{
auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments);
auto output = compileAndRunWithoutCheck(m_source, _value, _contractName, _arguments, _libraries);
return !output.empty() && m_transactionSuccessful;
}

View File

@ -60,7 +60,7 @@ public:
/// Compiles and deploys currently held source.
/// Returns true if deployment was successful, false otherwise.
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments);
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, dev::test::Address> const& _libraries = {});
private:
std::string m_source;

View File

@ -118,7 +118,7 @@ bytes compileFirstExpression(
ErrorReporter errorReporter(errors);
GlobalContext globalContext;
map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes;
NameAndTypeResolver resolver(globalContext, scopes, errorReporter);
NameAndTypeResolver resolver(globalContext, dev::test::Options::get().evmVersion(), scopes, errorReporter);
resolver.registerDeclarations(*sourceUnit);
vector<ContractDefinition const*> inheritanceHierarchy;

View File

@ -0,0 +1,56 @@
library L {
function f(uint256 v) external pure returns (uint) {
return v * v;
}
function g(uint256 v) external returns (uint) {
return v * v;
}
}
contract C {
function addr() public view returns (bool) {
return address(L) == address(0);
}
function g(uint256 v) public view returns (uint256) {
return L.f(v);
}
function h(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("f(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function i(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("f(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function j(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).delegatecall(abi.encodeWithSignature("g(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
function k(uint256 v) public returns (uint256) {
(bool success, bytes memory result) = address(L).call(abi.encodeWithSignature("g(uint256)", v));
assert(success);
return abi.decode(result, (uint256));
}
}
// ====
// EVMVersion: >=byzantium
// ----
// library: L
// addr() -> false
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16
// h(uint256): 1 -> 1
// h(uint256): 2 -> 4
// h(uint256): 4 -> 16
// i(uint256): 1 -> 1
// i(uint256): 2 -> 4
// i(uint256): 4 -> 16
// j(uint256): 1 -> 1
// j(uint256): 2 -> 4
// j(uint256): 4 -> 16
// k(uint256): 1 -> FAILURE
// k(uint256): 2 -> FAILURE
// k(uint256): 4 -> FAILURE

View File

@ -0,0 +1,24 @@
library L {
function f(uint256 a, uint256 b) external {
assert(a * a == b);
}
}
contract C {
function addr() public view returns (bool) {
return address(L) == address(0);
}
function g(uint256 a, uint256 b) public returns (bool) {
(bool success,) = address(L).delegatecall(abi.encodeWithSignature("f(uint256,uint256)", a, b));
return success;
}
}
// ----
// library: L
// g(uint256,uint256): 1, 1 -> true
// g(uint256,uint256): 1, 2 -> false
// g(uint256,uint256): 2, 3 -> false
// g(uint256,uint256): 2, 4 -> true
// g(uint256,uint256): 2, 5 -> false
// g(uint256,uint256): 4, 15 -> false
// g(uint256,uint256): 4, 16 -> true
// g(uint256,uint256): 4, 17 -> false

View File

@ -0,0 +1,13 @@
library L {
function f(uint256 v) external returns (uint256) { return v*v; }
}
contract C {
function g(uint256 v) external returns (uint256) {
return L.f(v);
}
}
// ----
// library: L
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16

View File

@ -0,0 +1,12 @@
library L {
function f(uint256 v) internal returns (uint256) { return v*v; }
}
contract C {
function g(uint256 v) external returns (uint256) {
return L.f(v);
}
}
// ----
// g(uint256): 1 -> 1
// g(uint256): 2 -> 4
// g(uint256): 4 -> 16

View File

@ -12,17 +12,33 @@ contract C {
x = true;
require(true);
}
/* Not properly supported by test system yet
function f2(bool a) public pure returns (bool x) {
x = a;
string memory message;
message = "fancy message!";
require(a, message);
}*/
}
function f3(bool a) public pure returns (bool x) {
x = a;
require(a, "msg");
}
function f4(bool a) public pure returns (bool x) {
x = a;
string memory message;
require(a, message);
}
}
// ====
// compileViaYul: true
// compileViaYul: also
// EVMVersion: >=byzantium
// ----
// f(bool): true -> true
// f(bool): false -> FAILURE
// fail() -> FAILURE
// succeed() -> true
// f2(bool): true -> true
// f2(bool): false -> FAILURE, hex"08c379a0", 0x20, 14, "fancy message!"
// f3(bool): true -> true
// f3(bool): false -> FAILURE, hex"08c379a0", 0x20, 3, "msg"
// f4(bool): true -> true
// f4(bool): false -> FAILURE, hex"08c379a0", 0x20, 0

View File

@ -0,0 +1,10 @@
pragma experimental SMTChecker;
contract C {
function f(address a, function(uint) external g) internal pure {
address b = address(g);
assert(a == b);
}
}
// ----
// Warning: (128-138): Type conversion is not yet fully supported and might yield false positives.
// Warning: (142-156): Assertion violation happens here

View File

@ -0,0 +1,8 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external returns (uint) g, function(uint) external returns (uint) h) public {
assert(g(2) == h(2));
}
}
// ----
// Warning: (155-175): Assertion violation happens here

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) returns (uint) g, function(uint) returns (uint) h) internal {
assert(g(2) == h(2));
assert(g == h);
}
}
// ----
// Warning: (146-150): Assertion checker does not yet implement this type of function call.
// Warning: (154-158): Assertion checker does not yet implement this type of function call.
// Warning: (170-176): Assertion checker does not yet implement the type function (uint256) returns (uint256) for comparisons
// Warning: (139-159): Assertion violation happens here
// Warning: (163-177): Assertion violation happens here

View File

@ -0,0 +1,14 @@
pragma experimental SMTChecker;
contract B {
function f() pure public {
g("0123456");
}
function g(bytes7 a) pure public {
assert(a == "0123456");
assert(a == "1234567");
}
}
// ----
// Warning: (162-184): Assertion violation happens here
// Warning: (136-158): Assertion violation happens here
// Warning: (162-184): Assertion violation happens here

View File

@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract B {
function f() mod2("0123456") pure public { }
modifier mod2(bytes7 a) {
assert(a == "0123456");
assert(a == "1234567");
_;
}
}
// ----
// Warning: (152-174): Assertion violation happens here

View File

@ -0,0 +1,12 @@
pragma experimental SMTChecker;
contract C {
function g() public pure returns (bytes32 val) { return "abc"; }
function f1() public pure returns (bytes32 val) { return g(); }
function a() public pure {
assert(f1() == "abc");
assert(f1() == "cde");
}
}
// ----
// Warning: (238-259): Assertion violation happens here

View File

@ -0,0 +1,15 @@
pragma experimental SMTChecker;
contract C {
function h() public pure returns (bytes32 val, bytes3 val2) { return ("abc", "def"); }
function g() public pure returns (bytes32 val) { return "abc"; }
function f1() public pure returns (bytes32 val) { return g(); }
function f2() public pure returns (bytes32 val, bytes3 val2) { return h(); }
function a() public pure {
(bytes32 v1, bytes3 v2) = f2();
assert(v1 == "abc");
assert(v2 == "cde");
}
}
// ----
// Warning: (442-461): Assertion violation happens here

View File

@ -0,0 +1,5 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external g) public {
}
}

View File

@ -0,0 +1,13 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function f(function(uint) internal g) internal {
g(2);
}
function h() public {
f(m_g);
}
}
// ----
// Warning: (121-125): Assertion checker does not yet implement this type of function call.
// Warning: (121-125): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,11 @@
pragma experimental SMTChecker;
contract C {
function f(function(uint) external payable g) internal {
g.selector;
g.gas(2).value(3)(4);
}
}
// ----
// Warning: (108-118): Assertion checker does not yet support this expression.
// Warning: (122-130): Assertion checker does not yet implement this type of function call.
// Warning: (122-139): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,22 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function f1(function(uint) internal g1) internal {
g1(2);
}
function f2(function(function(uint) internal) internal g2) internal {
g2(m_g);
}
function h() public {
f2(f1);
}
}
// ----
// Warning: (123-128): Assertion checker does not yet implement this type of function call.
// Warning: (152-197): Assertion checker does not yet support the type of this variable.
// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-219): Assertion checker does not yet implement this type of function call.
// Warning: (255-257): Internal error: Expression undefined for SMT solver.
// Warning: (255-257): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-214): Assertion checker does not yet implement type function (function (uint256))
// Warning: (212-219): Assertion checker does not yet implement this type of function call.

View File

@ -0,0 +1,27 @@
pragma experimental SMTChecker;
contract C {
function(uint) m_g;
function r() internal view returns (function(uint)) {
return m_g;
}
function f1(function(uint) internal g1) internal {
g1(2);
}
function f2(function(function(uint) internal) internal g2) internal {
g2(r());
}
function h() public {
f2(f1);
}
}
// ----
// Warning: (195-200): Assertion checker does not yet implement this type of function call.
// Warning: (224-269): Assertion checker does not yet support the type of this variable.
// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256))
// Warning: (287-288): Assertion checker does not yet support this global variable.
// Warning: (284-291): Assertion checker does not yet implement this type of function call.
// Warning: (327-329): Internal error: Expression undefined for SMT solver.
// Warning: (327-329): Assertion checker does not yet implement type function (function (uint256))
// Warning: (284-286): Assertion checker does not yet implement type function (function (uint256))
// Warning: (287-288): Assertion checker does not yet support this global variable.
// Warning: (284-291): Assertion checker does not yet implement this type of function call.

View File

@ -1,12 +1,12 @@
contract C {
function f() pure external {
function f() pure external returns (uint id) {
assembly {
pop(chainid())
id := chainid()
}
}
function g() view external {
function g() view external returns (uint sb) {
assembly {
pop(selfbalance())
sb := selfbalance()
}
}
}

View File

@ -0,0 +1,17 @@
contract C {
function f() pure external returns (uint id) {
assembly {
id := chainid()
}
}
function g() view external returns (uint sb) {
assembly {
sb := selfbalance()
}
}
}
// ====
// EVMVersion: =petersburg
// ----
// TypeError: (101-110): The "chainid" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg").
// TypeError: (215-228): The "selfbalance" instruction is only available for Istanbul-compatible VMs (you are currently compiling for "petersburg").

View File

@ -6,4 +6,5 @@ contract test {
}
}
// ----
// TypeError: (87-90): The type of a variable cannot be a library.
// TypeError: (100-103): Member "l" not found or not visible after argument-dependent lookup in library L.

View File

@ -0,0 +1,14 @@
library X { }
contract Y {
X abc;
function foo(X param) private view
{
X ofg;
ofg = abc;
}
}
// ----
// TypeError: (29-34): The type of a variable cannot be a library.
// TypeError: (50-57): The type of a variable cannot be a library.
// TypeError: (77-82): The type of a variable cannot be a library.

View File

@ -0,0 +1,8 @@
library L {
}
contract C {
function f() public pure returns (address) {
return address(L);
}
}
// ----

View File

@ -0,0 +1,9 @@
library L {
}
contract C {
function f() public pure returns (address payable) {
return address(L);
}
}
// ----
// TypeError: (99-109): Return argument type address is not implicitly convertible to expected type (type of first return variable) address payable.

View File

@ -58,6 +58,7 @@ namespace test
K(Boolean, "boolean", 0) \
/* special keywords */ \
K(Left, "left", 0) \
K(Library, "library", 0) \
K(Right, "right", 0) \
K(Failure, "FAILURE", 0) \
@ -268,6 +269,8 @@ struct FunctionCall
/// Marks this function call as "short-handed", meaning
/// no `->` declared.
bool omitsArrow = true;
/// Marks a library deployment call.
bool isLibrary = false;
};
}

View File

@ -74,6 +74,16 @@ vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls(siz
m_lineNumber++;
try
{
if (accept(Token::Library, true))
{
expect(Token::Colon);
call.signature = m_scanner.currentLiteral();
expect(Token::Identifier);
call.isLibrary = true;
call.expectations.failure = false;
}
else
{
tie(call.signature, call.useCallWithoutSignature) = parseFunctionSignature();
if (accept(Token::Comma, true))
@ -113,6 +123,8 @@ vector<dev::solidity::test::FunctionCall> TestFileParser::parseFunctionCalls(siz
if (call.signature == "constructor()")
call.isConstructor = true;
}
calls.emplace_back(std::move(call));
}
catch (Error const& _e)
@ -456,6 +468,7 @@ void TestFileParser::Scanner::scanNextToken()
if (_literal == "false") return TokenDesc{Token::Boolean, _literal};
if (_literal == "ether") return TokenDesc{Token::Ether, _literal};
if (_literal == "left") return TokenDesc{Token::Left, _literal};
if (_literal == "library") return TokenDesc{Token::Library, _literal};
if (_literal == "right") return TokenDesc{Token::Right, _literal};
if (_literal == "hex") return TokenDesc{Token::Hex, _literal};
if (_literal == "FAILURE") return TokenDesc{Token::Failure, _literal};

View File

@ -57,7 +57,9 @@ void testFunctionCall(
u256 _value = 0,
string _argumentComment = "",
string _expectationComment = "",
vector<string> _rawArguments = vector<string>{}
vector<string> _rawArguments = vector<string>{},
bool _isConstructor = false,
bool _isLibrary = false
)
{
BOOST_REQUIRE_EQUAL(_call.expectations.failure, _failure);
@ -79,6 +81,9 @@ void testFunctionCall(
++index;
}
}
BOOST_REQUIRE_EQUAL(_call.isConstructor, _isConstructor);
BOOST_REQUIRE_EQUAL(_call.isLibrary, _isLibrary);
}
BOOST_AUTO_TEST_SUITE(TestFileParserTest)
@ -883,6 +888,51 @@ BOOST_AUTO_TEST_CASE(call_unexpected_character)
BOOST_REQUIRE_THROW(parse(source), langutil::Error);
}
BOOST_AUTO_TEST_CASE(constructor)
{
char const* source = R"(
// constructor()
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"constructor()",
false,
{},
{},
0,
"",
"",
{},
true
);
}
BOOST_AUTO_TEST_CASE(library)
{
char const* source = R"(
// library: L
)";
auto const calls = parse(source);
BOOST_REQUIRE_EQUAL(calls.size(), 1);
testFunctionCall(
calls.at(0),
Mode::SingleLine,
"L",
false,
{},
{},
0,
"",
"",
{},
false,
true
);
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -55,6 +55,12 @@ string TestFunctionCall::format(
string newline = formatToken(Token::Newline);
string failure = formatToken(Token::Failure);
if (m_call.isLibrary)
{
stream << _linePrefix << newline << ws << "library:" << ws << m_call.signature;
return;
}
/// Formats the function signature. This is the same independent from the display-mode.
stream << _linePrefix << newline << ws << m_call.signature;
if (m_call.value > u256(0))