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

This commit is contained in:
chriseth 2020-06-30 18:56:51 +02:00
commit 3e3f9a472f
50 changed files with 701 additions and 40 deletions

View File

@ -40,6 +40,8 @@ env:
- ENCRYPTION_LABEL="6d4541b72666"
- SOLC_BUILD_TYPE=RelWithDebInfo
- SOLC_EMSCRIPTEN=Off
# FIXME: Pushing solcjson.js to solc-bin disabled until we fix the cause of #9261
- SOLC_PUBLISH_EMSCRIPTEN=Off
- SOLC_INSTALL_DEPS_TRAVIS=On
- SOLC_RELEASE=On
- SOLC_TESTS=On
@ -104,12 +106,12 @@ matrix:
sudo: required
compiler: gcc
node_js:
- "8"
- "10"
services:
- docker
before_install:
- nvm install 8
- nvm use 8
- nvm install 10
- nvm use 10
- docker pull ethereum/solidity-buildpack-deps:emsdk-1.39.15-2
env:
- SOLC_EMSCRIPTEN=On
@ -213,7 +215,7 @@ deploy:
# scripts because TravisCI doesn't provide much in the way of conditional logic.
- provider: script
script: test $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh)
script: test $SOLC_PUBLISH_EMSCRIPTEN != On || $SOLC_EMSCRIPTEN != On || (scripts/release_emscripten.sh)
skip_cleanup: true
on:
branch:

View File

@ -26,12 +26,14 @@ Bugfixes:
Language Features:
* General: Add unit denomination ``gwei``
Compiler Features:
* NatSpec: Add fields "kind" and "version" to the JSON output.
* NatSpec: Inherit tags from unique base if derived function does not provide any.
* Commandline Interface: Prevent some incompatible commandline options from being used together.
* NatSpec: Support NatSpec comments on events.
Bugfixes:
* NatSpec: Do not consider ``////`` and ``/***`` as NatSpec comments.

View File

@ -359,10 +359,10 @@ subAssembly
: 'assembly' identifier assemblyBlock ;
numberLiteral
: (DecimalNumber | HexNumber) NumberUnit? ;
: (DecimalNumber | HexNumber) (NumberUnit | Gwei)?;
identifier
: ('from' | 'calldata' | 'address' | Identifier) ;
: (Gwei | 'from' | 'calldata' | 'address' | Identifier) ;
BooleanLiteral
: 'true' | 'false' ;
@ -385,6 +385,8 @@ NumberUnit
: 'wei' | 'szabo' | 'finney' | 'ether'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' ;
Gwei: 'gwei' ;
HexLiteralFragment
: 'hex' (('"' HexDigits? '"') | ('\'' HexDigits? '\'')) ;

View File

@ -42,10 +42,6 @@ The following example shows a contract and a function using all available tags.
.. note::
NatSpec currently does NOT apply to public state variables (see
`solidity#3418 <https://github.com/ethereum/solidity/issues/3418>`__),
even if they are declared public and therefore do affect the ABI.
The Solidity compiler only interprets tags if they are external or
public. You are welcome to use similar comments for your internal and
private functions, but those will not be parsed.
@ -60,7 +56,6 @@ The following example shows a contract and a function using all available tags.
/// @notice You can use this contract for only the most basic simulation
/// @dev All function calls are currently implemented without side effects
contract Tree {
/// @author Mary A. Botanist
/// @notice Calculate tree age in years, rounded up, for live trees
/// @dev The Alexandr N. Tetearing algorithm could increase precision
/// @param rings The number of rings from dendrochronological sample
@ -84,10 +79,10 @@ in the same way as if it were tagged with ``@notice``.
Tag Context
=========== =============================================================================== =============================
``@title`` A title that should describe the contract/interface contract, interface
``@author`` The name of the author contract, interface, function
``@notice`` Explain to an end user what this does contract, interface, function, public state variable
``@dev`` Explain to a developer any extra details contract, interface, function, state variable
``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function
``@author`` The name of the author contract, interface
``@notice`` Explain to an end user what this does contract, interface, function, public state variable, event
``@dev`` Explain to a developer any extra details contract, interface, function, state variable, event
``@param`` Documents a parameter just like in doxygen (must be followed by parameter name) function, event
``@return`` Documents the return variables of a contract's function function, public state variable
=========== =============================================================================== =============================
@ -127,9 +122,11 @@ documentation and you may read more at
Inheritance Notes
-----------------
Currently it is undefined whether a contract with a function having no
NatSpec will inherit the NatSpec of a parent contract/interface for that
same function.
Functions without NatSpec will automatically inherit the documentation of their
base function. Exceptions to this are:
* When the parameter names are different.
* When there is more than one base function.
.. _header-output:
@ -193,7 +190,6 @@ file should also be produced and should look like this:
{
"age(uint256)" :
{
"author" : "Mary A. Botanist",
"details" : "The Alexandr N. Tetearing algorithm could increase precision",
"params" :
{

View File

@ -7,11 +7,12 @@ Units and Globally Available Variables
Ether Units
===========
A literal number can take a suffix of ``wei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
A literal number can take a suffix of ``wei``, ``gwei``, ``finney``, ``szabo`` or ``ether`` to specify a subdenomination of Ether, where Ether numbers without a postfix are assumed to be Wei.
::
assert(1 wei == 1);
assert(1 gwei == 1e9);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);

View File

@ -180,7 +180,11 @@ appropriate ``PUSHi`` instruction. In the following example,
``3`` and ``2`` are added resulting in 5 and then the
bitwise ``and`` with the string "abc" is computed.
The final value is assigned to a local variable called ``x``.
Strings are stored left-aligned and cannot be longer than 32 bytes.
The limit does not apply to string literals passed to builtin functions that require
literal arguments (e.g. ``setimmutable`` or ``loadimmutable``). Those strings never end up in the
generated bytecode.
.. code-block:: yul
@ -923,6 +927,30 @@ will store ``value`` at all points in memory that contain a call to
``loadimmutable("name")``.
linkersymbol
^^^^^^^^^^^^
The function ``linkersymbol("fq_library_name")`` is a placeholder for an address literal to be
substituted by the linker. Its first and only argument must be a string literal and represents the
fully qualified library name used with the ``--libraries`` option.
For example this code
.. code-block:: yul
let a := linkersymbol("file.sol:Math")
is equivalent to
.. code-block:: yul
let a := 0x1234567890123456789012345678901234567890
when the linker is invoked with ``--libraries "file.sol:Math:0x1234567890123456789012345678901234567890``
option.
See :ref:`Using the Commandline Compiler <commandline-compiler>` for details about the Solidity linker.
.. _yul-object:

View File

@ -232,6 +232,7 @@ namespace solidity::langutil
\
/* Identifiers (not keywords or future reserved words). */ \
T(Identifier, nullptr, 0) \
T(SubGwei, "gwei", 0) \
\
/* Keywords reserved for future use. */ \
K(After, "after", 0) \

View File

@ -32,6 +32,33 @@ using namespace solidity;
using namespace solidity::langutil;
using namespace solidity::frontend;
namespace
{
void copyMissingTags(StructurallyDocumentedAnnotation& _target, set<CallableDeclaration const*> const& _baseFunctions)
{
if (_baseFunctions.size() != 1)
return;
auto& sourceDoc = dynamic_cast<StructurallyDocumentedAnnotation const&>((*_baseFunctions.begin())->annotation());
set<string> existingTags;
for (auto const& iterator: _target.docTags)
existingTags.insert(iterator.first);
for (auto const& [tag, content]: sourceDoc.docTags)
if (tag != "inheritdoc" && !existingTags.count(tag))
_target.docTags.emplace(tag, content);
}
bool parameterNamesEqual(CallableDeclaration const& _a, CallableDeclaration const& _b)
{
return boost::range::equal(_a.parameters(), _b.parameters(), [](auto const& pa, auto const& pb) { return pa->name() == pb->name(); });
}
}
bool DocStringAnalyser::analyseDocStrings(SourceUnit const& _sourceUnit)
{
auto errorWatcher = m_errorReporter.errorWatcher();
@ -65,7 +92,24 @@ bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
if (_variable.isPublic())
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables");
else
{
parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables");
if (_variable.annotation().docTags.count("notice") > 0)
m_errorReporter.warning(
7816_error, _variable.documentation()->location(),
"Documentation tag on non-public state variables will be disallowed in 0.7.0. "
"You will need to use the @dev tag explicitly."
);
}
if (_variable.annotation().docTags.count("title") > 0 || _variable.annotation().docTags.count("author") > 0)
m_errorReporter.warning(
8532_error, _variable.documentation()->location(),
"Documentation tag @title and @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
);
if (_variable.annotation().docTags.empty())
copyMissingTags(_variable.annotation(), _variable.annotation().baseFunctions);
}
return false;
}
@ -129,6 +173,20 @@ void DocStringAnalyser::handleCallable(
static set<string> const validTags = set<string>{"author", "dev", "notice", "return", "param"};
parseDocStrings(_node, _annotation, validTags, "functions");
checkParameters(_callable, _node, _annotation);
if (
_annotation.docTags.empty() &&
_callable.annotation().baseFunctions.size() == 1 &&
parameterNamesEqual(_callable, **_callable.annotation().baseFunctions.begin())
)
copyMissingTags(_annotation, _callable.annotation().baseFunctions);
if (_node.documentation() && _annotation.docTags.count("author") > 0)
m_errorReporter.warning(
9843_error, _node.documentation()->location(),
"Documentation tag @author is only allowed on contract definitions. "
"It will be disallowed in 0.7.0."
);
}
void DocStringAnalyser::parseDocStrings(

View File

@ -2084,6 +2084,7 @@ public:
{
None = static_cast<int>(Token::Illegal),
Wei = static_cast<int>(Token::SubWei),
Gwei = static_cast<int>(Token::SubGwei),
Szabo = static_cast<int>(Token::SubSzabo),
Finney = static_cast<int>(Token::SubFinney),
Ether = static_cast<int>(Token::SubEther),

View File

@ -1002,6 +1002,8 @@ Literal::SubDenomination ASTJsonImporter::subdenomination(Json::Value const& _no
if (subDenStr == "wei")
return Literal::SubDenomination::Wei;
else if (subDenStr == "gwei")
return Literal::SubDenomination::Gwei;
else if (subDenStr == "szabo")
return Literal::SubDenomination::Szabo;
else if (subDenStr == "finney")

View File

@ -889,6 +889,9 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
case Literal::SubDenomination::Wei:
case Literal::SubDenomination::Second:
break;
case Literal::SubDenomination::Gwei:
value *= bigint("1000000000");
break;
case Literal::SubDenomination::Szabo:
value *= bigint("1000000000000");
break;

View File

@ -307,11 +307,6 @@ bool CompilerStack::analyze()
if (source->ast && !syntaxChecker.checkSyntax(*source->ast))
noErrors = false;
DocStringAnalyser docStringAnalyser(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast))
noErrors = false;
m_globalContext = make_shared<GlobalContext>();
// We need to keep the same resolver during the whole process.
NameAndTypeResolver resolver(*m_globalContext, m_evmVersion, m_errorReporter);
@ -367,6 +362,11 @@ bool CompilerStack::analyze()
if (!contractLevelChecker.check(*contract))
noErrors = false;
DocStringAnalyser docStringAnalyser(m_errorReporter);
for (Source const* source: m_sourceOrder)
if (source->ast && !docStringAnalyser.analyseDocStrings(*source->ast))
noErrors = false;
// New we run full type checks that go down to the expression level. This
// cannot be done earlier, because we need cross-contract types and information
// about whether a contract is abstract for the `new` expression.

View File

@ -36,6 +36,7 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
{
Json::Value doc;
Json::Value methods(Json::objectValue);
Json::Value events(Json::objectValue);
doc["version"] = Json::Value(c_natspecVersion);
doc["kind"] = Json::Value("user");
@ -84,7 +85,17 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
}
}
}
for (auto const& event: _contractDef.events())
{
string value = extractDoc(event->annotation().docTags, "notice");
if (!value.empty())
events[event->functionType(true)->externalSignature()]["notice"] = value;
}
doc["methods"] = methods;
if (!events.empty())
doc["events"] = events;
return doc;
}
@ -145,9 +156,16 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
stateVariables[varDecl->name()]["return"] = extractDoc(varDecl->annotation().docTags, "return");
}
Json::Value events(Json::objectValue);
for (auto const& event: _contractDef.events())
if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty())
events[event->functionType(true)->externalSignature()] = devDoc;
doc["methods"] = methods;
if (!stateVariables.empty())
doc["stateVariables"] = stateVariables;
if (!events.empty())
doc["events"] = events;
return doc;
}

View File

@ -1825,11 +1825,16 @@ ASTPointer<Expression> Parser::parsePrimaryExpression()
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance());
break;
case Token::Number:
if (TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken()))
if (
(m_scanner->peekNextToken() == Token::Identifier && m_scanner->peekLiteral() == "gwei") ||
TokenTraits::isEtherSubdenomination(m_scanner->peekNextToken())
)
{
ASTPointer<ASTString> literal = getLiteralAndAdvance();
nodeFactory.markEndPosition();
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(m_scanner->currentToken());
Token actualToken = m_scanner->currentToken() == Token::Identifier ? Token::SubGwei : m_scanner->currentToken();
Literal::SubDenomination subdenomination = static_cast<Literal::SubDenomination>(actualToken);
m_scanner->next();
expression = nodeFactory.createNode<Literal>(token, literal, subdenomination);
}

View File

@ -301,10 +301,15 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
for (size_t i = _funCall.arguments.size(); i > 0; i--)
{
Expression const& arg = _funCall.arguments[i - 1];
bool isLiteralArgument = needsLiteralArguments && (*needsLiteralArguments)[i - 1];
bool isStringLiteral = holds_alternative<Literal>(arg) && get<Literal>(arg).kind == LiteralKind::String;
argTypes.emplace_back(expectExpression(arg));
if (isLiteralArgument && isStringLiteral)
argTypes.emplace_back(expectUnlimitedStringLiteral(get<Literal>(arg)));
else
argTypes.emplace_back(expectExpression(arg));
if (needsLiteralArguments && (*needsLiteralArguments)[i - 1])
if (isLiteralArgument)
{
if (!holds_alternative<Literal>(arg))
m_errorReporter.typeError(
@ -433,6 +438,14 @@ YulString AsmAnalyzer::expectExpression(Expression const& _expr)
return types.empty() ? m_dialect.defaultType : types.front();
}
YulString AsmAnalyzer::expectUnlimitedStringLiteral(Literal const& _literal)
{
yulAssert(_literal.kind == LiteralKind::String, "");
yulAssert(m_dialect.validTypeForLiteral(LiteralKind::String, _literal.value, _literal.type), "");
return {_literal.type};
}
void AsmAnalyzer::expectBoolExpression(Expression const& _expr)
{
YulString type = expectExpression(_expr);
@ -531,7 +544,7 @@ void AsmAnalyzer::expectType(YulString _expectedType, YulString _givenType, Sour
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
{
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
if (builtin)
if (builtin && builtin->instruction.has_value())
return warnOnInstructions(builtin->instruction.value(), _location);
else
return false;

View File

@ -97,6 +97,7 @@ private:
/// Visits the expression, expects that it evaluates to exactly one value and
/// returns the type. Reports errors on errors and returns the default type.
YulString expectExpression(Expression const& _expr);
YulString expectUnlimitedStringLiteral(Literal const& _literal);
/// Vists the expression and expects it to return a single boolean value.
/// Reports an error otherwise.
void expectBoolExpression(Expression const& _expr);

View File

@ -124,6 +124,17 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
)
builtins.emplace(createEVMFunction(instr.first, instr.second));
builtins.emplace(createFunction("linkersymbol", 1, 1, SideEffects{}, {true}, [](
FunctionCall const& _call,
AbstractAssembly& _assembly,
BuiltinContext&,
function<void(Expression const&)>
) {
yulAssert(_call.arguments.size() == 1, "");
Expression const& arg = _call.arguments.front();
_assembly.appendLinkerSymbol(std::get<Literal>(arg).value.str());
}));
if (_objectAccess)
{
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, [](

View File

@ -59,6 +59,7 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
#include <boost/algorithm/string.hpp>
#ifdef _WIN32 // windows
@ -1202,9 +1203,14 @@ bool CommandLineInterface::processInput()
};
if (countEnabledOptions(nonAssemblyModeOptions) >= 1)
{
auto optionEnabled = [&](string const& name){ return m_args.count(name) > 0; };
auto enabledOptions = boost::copy_range<vector<string>>(nonAssemblyModeOptions | boost::adaptors::filtered(optionEnabled));
serr() << "The following options are invalid in assembly mode: ";
serr() << joinOptionNames(nonAssemblyModeOptions) << ". ";
serr() << "Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
serr() << joinOptionNames(enabledOptions) << ".";
if (m_args.count(g_strOptimizeYul) || m_args.count(g_strNoOptimizeYul))
serr() << " Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
serr() << endl;
return false;
}

View File

@ -48,6 +48,7 @@ using Address = util::h160;
// The various denominations; here for ease of use where needed within code.
static const u256 wei = 1;
static const u256 shannon = u256("1000000000");
static const u256 gwei = shannon;
static const u256 szabo = shannon * 1000;
static const u256 finney = szabo * 1000;
static const u256 ether = finney * 1000;

View File

@ -0,0 +1 @@
--strict-assembly --optimize-yul

View File

@ -0,0 +1 @@
The following options are invalid in assembly mode: --optimize-yul. Optimization is disabled by default and can be enabled with --optimize.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
{
sstore(0, 1)
}

View File

@ -1 +1 @@
The following options are invalid in assembly mode: --output-dir, --gas, --combined-json, --optimize-yul, --no-optimize-yul. Optimization is disabled by default and can be enabled with --optimize.
The following options are invalid in assembly mode: --output-dir.

View File

@ -221,6 +221,21 @@ BOOST_AUTO_TEST_CASE(int_with_wei_ether_subdenomination)
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(int_with_gwei_ether_subdenomination)
{
char const* sourceCode = R"(
contract test {
function test () {
uint x = 1 gwei;
}
}
)";
bytes code = compileFirstExpression(sourceCode);
bytes expectation({uint8_t(Instruction::PUSH4), 0x3b, 0x9a, 0xca, 0x00});
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
BOOST_AUTO_TEST_CASE(int_with_szabo_ether_subdenomination)
{
char const* sourceCode = R"(

View File

@ -355,6 +355,52 @@ BOOST_AUTO_TEST_CASE(private_state_variable)
checkNatspec(sourceCode, "test", userDoc, true);
}
BOOST_AUTO_TEST_CASE(event)
{
char const* sourceCode = R"(
contract ERC20 {
/// @notice This event is emitted when a transfer occurs.
/// @param from The source account.
/// @param to The destination account.
/// @param amount The amount.
/// @dev A test case!
event Transfer(address indexed from, address indexed to, uint amount);
}
)";
char const* devDoc = R"ABCDEF(
{
"events":
{
"Transfer(address,address,uint256)":
{
"details": "A test case!",
"params":
{
"amount": "The amount.", "from": "The source account.", "to": "The destination account."
}
}
},
"methods": {}
}
)ABCDEF";
checkNatspec(sourceCode, "ERC20", devDoc, false);
char const* userDoc = R"ABCDEF(
{
"events":
{
"Transfer(address,address,uint256)":
{
"notice": "This event is emitted when a transfer occurs."
}
},
"methods": {}
}
)ABCDEF";
checkNatspec(sourceCode, "ERC20", userDoc, true);
}
BOOST_AUTO_TEST_CASE(dev_desc_after_nl)
{
char const* sourceCode = R"(
@ -1218,6 +1264,263 @@ BOOST_AUTO_TEST_CASE(slash3_slash4)
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_CASE(dev_default_inherit_variable)
{
char const *sourceCode = R"(
contract C {
/// @notice Hello world
/// @dev test
function x() virtual external returns (uint) {
return 1;
}
}
contract D is C {
uint public override x;
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"x()":
{
"details": "test"
}
}
})ABCDEF";
char const *natspec1 = R"ABCDEF({
"methods" : {},
"stateVariables" :
{
"x" :
{
"details" : "test"
}
}
})ABCDEF";
checkNatspec(sourceCode, "C", natspec, false);
checkNatspec(sourceCode, "D", natspec1, false);
}
BOOST_AUTO_TEST_CASE(user_default_inherit_variable)
{
char const *sourceCode = R"(
contract C {
/// @notice Hello world
/// @dev test
function x() virtual external returns (uint) {
return 1;
}
}
contract D is C {
uint public override x;
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"x()":
{
"notice": "Hello world"
}
}
})ABCDEF";
checkNatspec(sourceCode, "C", natspec, true);
checkNatspec(sourceCode, "D", natspec, true);
}
BOOST_AUTO_TEST_CASE(dev_default_inherit)
{
char const *sourceCode = R"(
interface ERC20 {
/// Transfer ``amount`` from ``msg.sender`` to ``to``.
/// Second line.
/// @author Programmer
/// @dev test
/// @param to address to transfer to
/// @param amount amount to transfer
function transfer(address to, uint amount) external returns (bool);
}
contract Middle is ERC20 {
function transfer(address to, uint amount) virtual override external returns (bool)
{
return false;
}
}
contract Token is Middle {
function transfer(address to, uint amount) override external returns (bool)
{
return false;
}
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"transfer(address,uint256)":
{
"author": "Programmer",
"details": "test",
"params":
{
"amount": "amount to transfer",
"to": "address to transfer to"
}
}
}
})ABCDEF";
checkNatspec(sourceCode, "ERC20", natspec, false);
checkNatspec(sourceCode, "Middle", natspec, false);
checkNatspec(sourceCode, "Token", natspec, false);
}
BOOST_AUTO_TEST_CASE(user_default_inherit)
{
char const *sourceCode = R"(
interface ERC20 {
/// Transfer ``amount`` from ``msg.sender`` to ``to``.
/// Second line.
/// @author Programmer
/// @dev test
/// @param to address to transfer to
/// @param amount amount to transfer
function transfer(address to, uint amount) external returns (bool);
}
contract Middle is ERC20 {
function transfer(address to, uint amount) virtual override external returns (bool)
{
return false;
}
}
contract Token is Middle {
function transfer(address to, uint amount) override external returns (bool)
{
return false;
}
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"transfer(address,uint256)":
{
"notice": "Transfer ``amount`` from ``msg.sender`` to ``to``. Second line."
}
}
})ABCDEF";
checkNatspec(sourceCode, "ERC20", natspec, true);
checkNatspec(sourceCode, "Middle", natspec, true);
checkNatspec(sourceCode, "Token", natspec, true);
}
BOOST_AUTO_TEST_CASE(dev_inherit_parameter_mismatch)
{
char const *sourceCode = R"(
interface ERC20 {
/// Transfer ``amount`` from ``msg.sender`` to ``to``.
/// @author Programmer
/// @dev test
/// @param to address to transfer to
/// @param amount amount to transfer
function transfer(address to, uint amount) external returns (bool);
}
contract Middle is ERC20 {
function transfer(address to, uint amount) override virtual external returns (bool) {
return false;
}
}
contract Token is Middle {
function transfer(address too, uint amount) override external returns (bool) {
return false;
}
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"transfer(address,uint256)":
{
"author": "Programmer",
"details": "test",
"params":
{
"amount": "amount to transfer",
"to": "address to transfer to"
}
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods": { }
})ABCDEF";
checkNatspec(sourceCode, "ERC20", natspec, false);
checkNatspec(sourceCode, "Middle", natspec, false);
checkNatspec(sourceCode, "Token", natspec2, false);
}
BOOST_AUTO_TEST_CASE(user_inherit_parameter_mismatch)
{
char const *sourceCode = R"(
interface ERC20 {
/// Transfer ``amount`` from ``msg.sender`` to ``to``.
/// @author Programmer
/// @dev test
/// @param to address to transfer to
/// @param amount amount to transfer
function transfer(address to, uint amount) external returns (bool);
}
contract Middle is ERC20 {
function transfer(address to, uint amount) override virtual external returns (bool) {
return false;
}
}
contract Token is Middle {
function transfer(address too, uint amount) override external returns (bool) {
return false;
}
}
)";
char const *natspec = R"ABCDEF({
"methods":
{
"transfer(address,uint256)":
{
"notice": "Transfer ``amount`` from ``msg.sender`` to ``to``."
}
}
})ABCDEF";
char const *natspec2 = R"ABCDEF({
"methods": { }
})ABCDEF";
checkNatspec(sourceCode, "ERC20", natspec, true);
checkNatspec(sourceCode, "Middle", natspec, true);
checkNatspec(sourceCode, "Token", natspec2, true);
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -428,8 +428,9 @@ BOOST_AUTO_TEST_CASE(comments_mixed_in_sequence)
BOOST_AUTO_TEST_CASE(ether_subdenominations)
{
Scanner scanner(CharStream("wei szabo finney ether", ""));
Scanner scanner(CharStream("wei gwei szabo finney ether", ""));
BOOST_CHECK_EQUAL(scanner.currentToken(), Token::SubWei);
BOOST_CHECK_EQUAL(scanner.next(), Token::Identifier);
BOOST_CHECK_EQUAL(scanner.next(), Token::SubSzabo);
BOOST_CHECK_EQUAL(scanner.next(), Token::SubFinney);
BOOST_CHECK_EQUAL(scanner.next(), Token::SubEther);

View File

@ -0,0 +1,10 @@
contract C {
uint constant gwei = 1 gwei;
function f() public view returns(uint) { return gwei; }
}
// ====
// compileViaYul: also
// ----
// f() -> 1000000000

View File

@ -2,5 +2,6 @@ contract C {
uint constant a = 1 wei + 2 szabo + 3 finney + 4 ether;
uint constant b = 1 seconds + 2 minutes + 3 hours + 4 days + 5 weeks;
uint constant c = 2 szabo / 1 seconds + 3 finney * 3 hours;
uint constant d = 2 gwei / 1 seconds + 3 finney * 3 hours;
}
// ----

View File

@ -0,0 +1,3 @@
contract C {
uint constant gwei = 1 gwei;
}

View File

@ -0,0 +1,4 @@
contract C {
uint immutable long___name___that___definitely___exceeds___the___thirty___two___byte___limit = 0;
}
// ----

View File

@ -0,0 +1,6 @@
contract C {
/// @author author
function iHaveAuthor() public pure {}
}
// ----
// Warning 9843: (17-35): Documentation tag @author is only allowed on contract definitions. It will be disallowed in 0.7.0.

View File

@ -1,15 +1,16 @@
contract c {
contract C {
function f() public
{
a = 1 wei;
b = 2 szabo;
c = 3 finney;
b = 4 ether;
d = 4 ether;
e = 5 gwei;
}
uint256 a;
uint256 b;
uint256 c;
uint256 d;
uint256 e;
}
// ----
// Warning 2519: (170-179): This declaration shadows an existing declaration.

View File

@ -0,0 +1,17 @@
object "a" {
code {
setimmutable(
"long___name___that___definitely___exceeds___the___thirty___two___byte___limit",
0x1234567890123456789012345678901234567890
)
}
}
// ----
// Assembly:
// /* "source":152:194 */
// 0x1234567890123456789012345678901234567890
// /* "source":32:204 */
// assignImmutable("0x85a5b1db611c82c46f5fa18e39ae218397536256c451e5de155a86de843a9ad6")
// Bytecode: 73123456789012345678901234567890123456789050
// Opcodes: PUSH20 0x1234567890123456789012345678901234567890 POP
// SourceMappings: 152:42:0:-:0;32:172

View File

@ -0,0 +1,42 @@
object "a" {
code {
let addr := linkersymbol("contract/test.sol:L")
mstore(128, shl(227, 0x18530aaf))
let success := call(gas(), addr, 0, 128, 4, 128, 0)
}
}
// ----
// Assembly:
// linkerSymbol("f919ba91ac99f96129544b80b9516b27a80e376b9dc693819d0b18b7e0395612")
// /* "source":109:119 */
// 0x18530aaf
// /* "source":104:107 */
// 0xe3
// /* "source":100:120 */
// shl
// /* "source":95:98 */
// 0x80
// /* "source":88:121 */
// mstore
// /* "source":179:180 */
// 0x00
// /* "source":174:177 */
// 0x80
// /* "source":171:172 */
// 0x04
// /* "source":166:169 */
// 0x80
// /* "source":163:164 */
// 0x00
// /* "source":157:161 */
// dup6
// /* "source":150:155 */
// gas
// /* "source":145:181 */
// call
// /* "source":22:187 */
// pop
// pop
// Bytecode: 7300000000000000000000000000000000000000006318530aaf60e31b60805260006080600460806000855af15050
// Opcodes: PUSH20 0x0 PUSH4 0x18530AAF PUSH1 0xE3 SHL PUSH1 0x80 MSTORE PUSH1 0x0 PUSH1 0x80 PUSH1 0x4 PUSH1 0x80 PUSH1 0x0 DUP6 GAS CALL POP POP
// SourceMappings: :::-:0;109:10:0;104:3;100:20;95:3;88:33;179:1;174:3;171:1;166:3;163:1;157:4;150:5;145:36;22:165;

View File

@ -0,0 +1,16 @@
{
mstore(0, balance(address()))
mstore(0x20, selfbalance())
mstore(0x40, balance(div(mul(address(), 2), 2)))
mstore(0x60, balance(add(address(), 1)))
}
// ====
// EVMVersion: >=istanbul
// ----
// Trace:
// Memory dump:
// 0: 0000000000000000000000000000000000000000000000000000000022223333
// 20: 0000000000000000000000000000000000000000000000000000000022223333
// 40: 0000000000000000000000000000000000000000000000000000000022223333
// 60: 0000000000000000000000000000000000000000000000000000000022222222
// Storage dump:

View File

@ -0,0 +1,6 @@
{
let x := byte(31, "11112222333344445555666677778888")
}
// ====
// dialect: evm
// ----

View File

@ -0,0 +1,6 @@
{
let addr := linkersymbol("contract/library.sol:L")
}
// ====
// dialect: evm
// ----

View File

@ -0,0 +1,6 @@
{
let addr:u256 := linkersymbol("contract/library.sol:L")
}
// ====
// dialect: evmTyped
// ----

View File

@ -0,0 +1,7 @@
{
linkersymbol("contract/library.sol:L")
}
// ====
// dialect: ewasm
// ----
// DeclarationError 4619: (6-18): Function not found.

View File

@ -0,0 +1,8 @@
{
let library_name := "contract/library.sol:L"
let addr := linkersymbol(library_name)
}
// ====
// dialect: evm
// ----
// TypeError 9114: (67-79): Function expects direct literals as arguments.

View File

@ -0,0 +1,6 @@
{
let addr := loadimmutable("address")
}
// ====
// dialect: evm
// ----

View File

@ -0,0 +1,7 @@
{
setimmutable(loadimmutable("abc"), "abc")
}
// ====
// dialect: evm
// ----
// TypeError 9114: (6-18): Function expects direct literals as arguments.

View File

@ -0,0 +1,6 @@
{
setimmutable("address", 0x1234567890123456789012345678901234567890)
}
// ====
// dialect: evm
// ----

View File

@ -0,0 +1,7 @@
{
let name := "long___name___that___definitely___exceeds___the___thirty___two___byte___limit"
}
// ====
// dialect: evm
// ----
// TypeError 3069: (18-97): String literal too long (77 > 32)

View File

@ -0,0 +1,7 @@
{
let x := byte(40, "long___value__that___definitely___exceeds___the___thirty___two___byte___limit")
}
// ====
// dialect: evm
// ----
// TypeError 3069: (24-103): String literal too long (77 > 32)

View File

@ -0,0 +1,9 @@
{
setimmutable(
"long___name___that___definitely___exceeds___the___thirty___two___byte___limit",
0x1234567890123456789012345678901234567890
)
}
// ====
// dialect: evm
// ----

View File

@ -0,0 +1,6 @@
{
let addr := linkersymbol("contract/long___name___that___definitely___exceeds___the___thirty___two___byte___limit.sol:L")
}
// ====
// dialect: evm
// ----

View File

@ -8,6 +8,7 @@
" ether "
" finney "
" gasleft() "
" gwei "
" hours "
" minutes "
" msg.data "

View File

@ -182,7 +182,10 @@ u256 EVMInstructionInterpreter::eval(
case Instruction::ADDRESS:
return m_state.address;
case Instruction::BALANCE:
return m_state.balance;
if (arg[0] == m_state.address)
return m_state.selfbalance;
else
return m_state.balance;
case Instruction::SELFBALANCE:
return m_state.selfbalance;
case Instruction::ORIGIN: