aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md8
-rw-r--r--circle.yml22
-rw-r--r--docs/contracts.rst23
-rw-r--r--docs/miscellaneous.rst8
-rw-r--r--docs/units-and-global-variables.rst3
-rw-r--r--libsolidity/analysis/GlobalContext.cpp1
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp3
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp19
-rw-r--r--libsolidity/analysis/TypeChecker.cpp39
-rw-r--r--libsolidity/ast/ExperimentalFeatures.h5
-rw-r--r--libsolidity/ast/Types.cpp3
-rw-r--r--libsolidity/ast/Types.h3
-rw-r--r--libsolidity/codegen/CompilerContext.cpp1
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp35
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp45
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h17
-rw-r--r--libsolidity/interface/AssemblyStack.cpp2
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp4
-rwxr-xr-xscripts/tests.sh13
-rw-r--r--test/libjulia/Common.cpp1
-rw-r--r--test/libjulia/Parser.cpp1
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp72
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp33
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp305
-rw-r--r--test/libsolidity/ViewPureChecker.cpp1
25 files changed, 604 insertions, 63 deletions
diff --git a/Changelog.md b/Changelog.md
index 67634ea0..2792c5e7 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,16 +2,21 @@
Features:
* C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
- * Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
+ * Code Generator: Assert that ``k != 0`` for ``mulmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
+ * Code Generator: Do not retain any gas in calls (except if EVM version is set to homestead).
+ * Code Generator: Use ``STATICCALL`` opcode for calling ``view`` and ``pure`` functions as experimenal 0.5.0 feature.
+ * Inline Assembly: Enforce strict mode as experimental 0.5.0 feature.
* Interface: Provide ability to select target EVM version (homestead or byzantium, with byzantium being the default).
* Standard JSON: Reject badly formatted invalid JSON inputs.
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
* Support and recommend using ``emit EventName();`` to call events explicitly.
* Syntax Analyser: Do not warn about experimental features if they do not concern code generation.
+ * Syntax Analyser: Do not warn about ``pragma experimental "v0.5.0"`` since it does not affect code generation.
* Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature.
* Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature.
* Syntax Checker: Issue warning when using overloads of ``address`` on contract instances.
* Type Checker: disallow combining hex numbers and unit denominations as experimental 0.5.0 feature.
+ * Improved messaging when error spans multiple lines of a sourcefile
Bugfixes:
* Assembly: Raise error on oversized number literals in assembly.
@@ -20,6 +25,7 @@ Bugfixes:
* Standalone Assembly: Do not ignore input after closing brace of top level block.
* Standard JSON: Catch errors properly when invalid "sources" are passed.
* Standard JSON: Ensure that library addresses supplied are of correct length and hex prefixed.
+ * Type Checker: Properly detect which array and struct types are unsupported by the old ABI encoder.
* Type Checker: Properly warn when using ``_offset`` and ``_slot`` for constants in inline assembly.
* Commandline interface: throw error if option is unknown
diff --git a/circle.yml b/circle.yml
index 1ed09ada..263cb700 100644
--- a/circle.yml
+++ b/circle.yml
@@ -96,14 +96,26 @@ jobs:
if [ "$CIRCLE_BRANCH" = release -o -n "$CIRCLE_TAG" ]; then echo -n > prerelease.txt; else date -u +"nightly.%Y.%-m.%-d" > prerelease.txt; fi
echo -n "$CIRCLE_SHA1" > commit_hash.txt
- restore_cache:
- key: ccache-{{ arch }}-{{ .Branch }}
- key: ccache-{{ arch }}
- key: ccache
+ key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
+ key: ccache-{{ arch }}-{{ .Branch }}-
+ key: ccache-{{ arch }}-develop-
+ key: ccache-{{ arch }}-
+ - run:
+ name: Configure ccache
+ command: ccache -M 200M && ccache -c && ccache -s && ccache -z
- run:
name: Build
- command: ./scripts/build.sh RelWithDebInfo
+ command: |
+ mkdir -p build
+ cd build
+ cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo
+ make -j4
+ - run:
+ name: CCache statistics
+ command: ccache -s
- save_cache:
- key: ccache-{{ arch }}-{{ .Branch }}
+ key: ccache-{{ arch }}-{{ .Branch }}-{{ .Environment.CIRCLE_SHA1 }}
+ when: always
paths:
- ~/.ccache
- store_artifacts:
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 12b785d5..121c4de0 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -402,7 +402,7 @@ State variables can be declared as ``constant``. In this case, they have to be
assigned from an expression which is a constant at compile time. Any expression
that accesses storage, blockchain data (e.g. ``now``, ``this.balance`` or
``block.number``) or
-execution data (``msg.gas``) or make calls to external contracts are disallowed. Expressions
+execution data (``msg.value`` or ``gasleft()``) or make calls to external contracts are disallowed. Expressions
that might have a side-effect on memory allocation are allowed, but those that
might have a side-effect on other memory objects are not. The built-in functions
``keccak256``, ``sha256``, ``ripemd160``, ``ecrecover``, ``addmod`` and ``mulmod``
@@ -472,8 +472,15 @@ The following statements are considered modifying the state:
.. note::
Getter methods are marked ``view``.
+.. note::
+ If invalid explicit type conversions are used, state modifications are possible
+ even though a ``view`` function was called.
+ You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
+ prevent modifications to the state on the level of the EVM by adding
+ ``pragma experimental "v0.5.0";``
+
.. warning::
- Before version 0.4.17 the compiler didn't enforce that ``view`` is not modifying the state.
+ The compiler does not enforce yet that a ``view`` method is not modifying state. It raises a warning though.
.. index:: ! pure function, function;pure
@@ -502,6 +509,18 @@ In addition to the list of state modifying statements explained above, the follo
}
}
+.. note::
+ If invalid explicit type conversions are used, state modifications are possible
+ even though a ``pure`` function was called.
+ You can switch the compiler to use ``STATICCALL`` when calling such functions and thus
+ prevent modifications to the state on the level of the EVM by adding
+ ``pragma experimental "v0.5.0";``
+
+.. warning::
+ It is not possible to prevent functions from reading the state at the level
+ of the EVM, it is only possible to prevent them from writing to the state
+ (i.e. only ``view`` can be enforced at the EVM level, ``pure`` can not).
+
.. warning::
Before version 0.4.17 the compiler didn't enforce that ``pure`` is not reading the state.
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 70ed6201..075b6be0 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -192,6 +192,11 @@ These are regular array indices into a list of source files usually called
``"sourceList"``, which is part of the combined-json and the output of
the json / npm compiler.
+.. note ::
+ In the case of instructions that are not associated with any particular source file,
+ the source mapping assigns an integer identifier of ``-1``. This may happen for
+ bytecode sections stemming from compiler-generated inline assembly statements.
+
The source mappings inside the AST use the following
notation:
@@ -315,8 +320,9 @@ Global Variables
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.timestamp`` (``uint``): current block timestamp
+- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata
-- ``msg.gas`` (``uint``): remaining gas
+- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
- ``msg.sender`` (``address``): sender of the message (current call)
- ``msg.value`` (``uint``): number of wei sent with the message
- ``now`` (``uint``): current block timestamp (alias for ``block.timestamp``)
diff --git a/docs/units-and-global-variables.rst b/docs/units-and-global-variables.rst
index cc4d4446..1b58b1e8 100644
--- a/docs/units-and-global-variables.rst
+++ b/docs/units-and-global-variables.rst
@@ -58,8 +58,9 @@ Block and Transaction Properties
- ``block.gaslimit`` (``uint``): current block gaslimit
- ``block.number`` (``uint``): current block number
- ``block.timestamp`` (``uint``): current block timestamp as seconds since unix epoch
+- ``gasleft() returns (uint256)``: remaining gas
- ``msg.data`` (``bytes``): complete calldata
-- ``msg.gas`` (``uint``): remaining gas
+- ``msg.gas`` (``uint``): remaining gas - deprecated in version 0.4.21 and to be replaced by ``gasleft()``
- ``msg.sender`` (``address``): sender of the message (current call)
- ``msg.sig`` (``bytes4``): first four bytes of the calldata (i.e. function identifier)
- ``msg.value`` (``uint``): number of wei sent with the message
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index fd39d860..34cb61d8 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -39,6 +39,7 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("assert", make_shared<FunctionType>(strings{"bool"}, strings{}, FunctionType::Kind::Assert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("block", make_shared<MagicType>(MagicType::Kind::Block)),
make_shared<MagicVariableDeclaration>("ecrecover", make_shared<FunctionType>(strings{"bytes32", "uint8", "bytes32", "bytes32"}, strings{"address"}, FunctionType::Kind::ECRecover, false, StateMutability::Pure)),
+ make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft, false, StateMutability::View)),
make_shared<MagicVariableDeclaration>("keccak256", make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Kind::SHA3, true, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("log0", make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Kind::Log0)),
make_shared<MagicVariableDeclaration>("log1", make_shared<FunctionType>(strings{"bytes32", "bytes32"}, strings{}, FunctionType::Kind::Log1)),
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 296a39c2..f91eaf6e 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -280,7 +280,8 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
// We use the latest EVM version because we will re-run it anyway.
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
+ boost::optional<Error::Type> errorTypeForLoose = m_experimental050Mode ? Error::Type::SyntaxError : Error::Type::Warning;
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, EVMVersion(), errorTypeForLoose, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
return false;
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index bd8ee597..d4de219a 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -139,6 +139,23 @@ bool StaticAnalyzer::visit(ExpressionStatement const& _statement)
bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
{
+ bool const v050 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
+ if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "gas")
+ {
+ if (v050)
+ m_errorReporter.typeError(
+ _memberAccess.location(),
+ "\"msg.gas\" has been deprecated in favor of \"gasleft()\""
+ );
+ else
+ m_errorReporter.warning(
+ _memberAccess.location(),
+ "\"msg.gas\" has been deprecated in favor of \"gasleft()\""
+ );
+ }
+
if (m_nonPayablePublic && !m_library)
if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
@@ -151,7 +168,7 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
if (type->kind() == FunctionType::Kind::BareCallCode)
{
- if (m_currentContract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
+ if (v050)
m_errorReporter.typeError(
_memberAccess.location(),
"\"callcode\" has been deprecated in favour of \"delegatecall\"."
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 106a2c8e..bebdb9b6 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -34,6 +34,29 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
+namespace
+{
+
+bool typeSupportedByOldABIEncoder(Type const& _type)
+{
+ if (_type.dataStoredIn(DataLocation::Storage))
+ return true;
+ else if (_type.category() == Type::Category::Struct)
+ return false;
+ else if (_type.category() == Type::Category::Array)
+ {
+ auto const& arrayType = dynamic_cast<ArrayType const&>(_type);
+ auto base = arrayType.baseType();
+ if (!typeSupportedByOldABIEncoder(*base))
+ return false;
+ else if (base->category() == Type::Category::Array && base->isDynamicallySized())
+ return false;
+ }
+ return true;
+}
+
+}
+
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
{
@@ -561,13 +584,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
if (
_function.visibility() > FunctionDefinition::Visibility::Internal &&
- type(*var)->category() == Type::Category::Struct &&
- !type(*var)->dataStoredIn(DataLocation::Storage) &&
- !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)
+ !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
+ !typeSupportedByOldABIEncoder(*type(*var))
)
m_errorReporter.typeError(
var->location(),
- "Structs are only supported in the new experimental ABI encoder. "
+ "This type is only supported in the new experimental ABI encoder. "
"Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
);
@@ -872,10 +894,15 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
};
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
_inlineAssembly.annotation().analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
+ boost::optional<Error::Type> errorTypeForLoose =
+ m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) ?
+ Error::Type::SyntaxError :
+ Error::Type::Warning;
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
m_evmVersion,
+ errorTypeForLoose,
assembly::AsmFlavour::Loose,
identifierAccess
);
@@ -2057,13 +2084,13 @@ void TypeChecker::endVisit(Literal const& _literal)
m_errorReporter.fatalTypeError(
_literal.location(),
"Hexadecimal numbers cannot be used with unit denominations. "
- "You can use an expression of the form '0x1234 * 1 day' instead."
+ "You can use an expression of the form \"0x1234 * 1 day\" instead."
);
else
m_errorReporter.warning(
_literal.location(),
"Hexadecimal numbers with unit denominations are deprecated. "
- "You can use an expression of the form '0x1234 * 1 day' instead."
+ "You can use an expression of the form \"0x1234 * 1 day\" instead."
);
}
if (!_literal.annotation().type)
diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h
index 3ecfac7b..30ea7ec5 100644
--- a/libsolidity/ast/ExperimentalFeatures.h
+++ b/libsolidity/ast/ExperimentalFeatures.h
@@ -29,8 +29,8 @@ namespace solidity
enum class ExperimentalFeature
{
- SMTChecker,
ABIEncoderV2, // new ABI encoder that makes use of JULIA
+ SMTChecker,
V050, // v0.5.0 breaking changes
Test,
TestOnlyAnalysis
@@ -40,12 +40,13 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
{
{ ExperimentalFeature::SMTChecker, true },
{ ExperimentalFeature::TestOnlyAnalysis, true },
+ { ExperimentalFeature::V050, true }
};
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
{
- { "SMTChecker", ExperimentalFeature::SMTChecker },
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
+ { "SMTChecker", ExperimentalFeature::SMTChecker },
{ "v0.5.0", ExperimentalFeature::V050 },
{ "__test", ExperimentalFeature::Test },
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 771ae643..c08e0e67 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -1589,8 +1589,6 @@ bool ArrayType::canBeUsedExternally(bool _inLibrary) const
return true;
else if (!m_baseType->canBeUsedExternally(_inLibrary))
return false;
- else if (m_baseType->category() == Category::Array && m_baseType->isDynamicallySized())
- return false;
else
return true;
}
@@ -2346,6 +2344,7 @@ string FunctionType::richIdentifier() const
case Kind::Log2: id += "log2"; break;
case Kind::Log3: id += "log3"; break;
case Kind::Log4: id += "log4"; break;
+ case Kind::GasLeft: id += "gasleft"; break;
case Kind::Event: id += "event"; break;
case Kind::SetGas: id += "setgas"; break;
case Kind::SetValue: id += "setvalue"; break;
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 7985521e..c20a025f 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -902,7 +902,8 @@ public:
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
ObjectCreation, ///< array creation using new
Assert, ///< assert()
- Require ///< require()
+ Require, ///< require()
+ GasLeft ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index ebf0213a..0bf88267 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -330,6 +330,7 @@ void CompilerContext::appendInlineAssembly(
analysisInfo,
errorReporter,
m_evmVersion,
+ boost::none,
assembly::AsmFlavour::Strict,
identifierAccess.resolve
).analyze(*parserResult);
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 12881d63..7162cb0d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -906,6 +906,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
m_context << success;
break;
}
+ case FunctionType::Kind::GasLeft:
+ m_context << Instruction::GAS;
+ break;
default:
solAssert(false, "Invalid function type.");
}
@@ -1607,6 +1610,10 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
+ bool useStaticCall =
+ _functionType.stateMutability() <= StateMutability::View &&
+ m_context.experimentalFeatureActive(ExperimentalFeature::V050) &&
+ m_context.evmVersion().hasStaticCall();
unsigned retSize = 0;
if (returnSuccessCondition)
@@ -1671,16 +1678,19 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().storeFreeMemoryPointer();
}
- // Touch the end of the output area so that we do not pay for memory resize during the call
- // (which we would have to subtract from the gas left)
- // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
- // would remove that, so we use MSTORE here.
- if (!_functionType.gasSet() && retSize > 0)
+ if (!m_context.evmVersion().canOverchargeGasForCall())
{
- m_context << u256(0);
- utils().fetchFreeMemoryPointer();
- // This touches too much, but that way we save some rounding arithmetics
- m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
+ // Touch the end of the output area so that we do not pay for memory resize during the call
+ // (which we would have to subtract from the gas left)
+ // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
+ // would remove that, so we use MSTORE here.
+ if (!_functionType.gasSet() && retSize > 0)
+ {
+ m_context << u256(0);
+ utils().fetchFreeMemoryPointer();
+ // This touches too much, but that way we save some rounding arithmetics
+ m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
+ }
}
// Copy function identifier to memory.
@@ -1732,6 +1742,8 @@ void ExpressionCompiler::appendExternalFunctionCall(
// [value,] addr, gas (stack top)
if (isDelegateCall)
solAssert(!_functionType.valueSet(), "Value set for delegatecall");
+ else if (useStaticCall)
+ solAssert(!_functionType.valueSet(), "Value set for staticcall");
else if (_functionType.valueSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos));
else
@@ -1749,7 +1761,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.gasSet())
m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos));
- else if (m_context.experimentalFeatureActive(ExperimentalFeature::V050))
+ else if (m_context.evmVersion().canOverchargeGasForCall())
// Send all gas (requires tangerine whistle EVM)
m_context << Instruction::GAS;
else
@@ -1763,10 +1775,13 @@ void ExpressionCompiler::appendExternalFunctionCall(
gasNeededByCaller += eth::GasCosts::callNewAccountGas; // we never know
m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB;
}
+ // Order is important here, STATICCALL might overlap with DELEGATECALL.
if (isDelegateCall)
m_context << Instruction::DELEGATECALL;
else if (isCallCode)
m_context << Instruction::CALLCODE;
+ else if (useStaticCall)
+ m_context << Instruction::STATICCALL;
else
m_context << Instruction::CALL;
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index a7f764a5..abf7ddf2 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -54,7 +54,10 @@ bool AsmAnalyzer::analyze(Block const& _block)
bool AsmAnalyzer::operator()(Label const& _label)
{
- solAssert(m_flavour == AsmFlavour::Loose, "");
+ checkLooseFeature(
+ _label.location,
+ "The use of labels is deprecated. Please use \"if\", \"switch\", \"for\" or function calls instead."
+ );
m_info.stackHeightInfo[&_label] = m_stackHeight;
warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location);
return true;
@@ -62,7 +65,10 @@ bool AsmAnalyzer::operator()(Label const& _label)
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
{
- solAssert(m_flavour == AsmFlavour::Loose, "");
+ checkLooseFeature(
+ _instruction.location,
+ "The use of non-functional instructions is deprecated. Please use functional notation instead."
+ );
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
@@ -170,18 +176,31 @@ bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
{
- size_t initialStackHeight = m_stackHeight;
+ int initialStackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, _statement.expression);
- if (m_flavour != AsmFlavour::Loose)
- if (!expectDeposit(0, initialStackHeight, _statement.location))
+ if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose))
+ {
+ Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
+ string msg =
+ "Top-level expressions are not supposed to return values (this expression returns " +
+ boost::lexical_cast<string>(m_stackHeight - initialStackHeight) +
+ " value" +
+ (m_stackHeight - initialStackHeight == 1 ? "" : "s") +
+ "). Use ``pop()`` or assign them.";
+ m_errorReporter.error(errorType, _statement.location, msg);
+ if (errorType != Error::Type::Warning)
success = false;
+ }
m_info.stackHeightInfo[&_statement] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
{
- solAssert(m_flavour == AsmFlavour::Loose, "");
+ checkLooseFeature(
+ _assignment.location,
+ "The use of stack assignment is deprecated. Please use assignment in functional notation instead."
+ );
bool success = checkAssignment(_assignment.variableName, size_t(-1));
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
return success;
@@ -577,10 +596,22 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
);
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
- m_errorReporter.warning(
+ {
+ solAssert(m_flavour == AsmFlavour::Loose, "");
+ m_errorReporter.error(
+ m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning,
_location,
"Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are discouraged. "
"Please consider using \"switch\", \"if\" or \"for\" statements instead."
);
+ }
+}
+
+void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
+{
+ if (m_flavour != AsmFlavour::Loose)
+ solAssert(false, _description);
+ else if (m_errorTypeForLoose)
+ m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index 867711c7..8d2a71f0 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -30,6 +30,7 @@
#include <libsolidity/inlineasm/AsmDataForward.h>
#include <boost/variant.hpp>
+#include <boost/optional.hpp>
#include <functional>
#include <memory>
@@ -56,9 +57,17 @@ public:
AsmAnalysisInfo& _analysisInfo,
ErrorReporter& _errorReporter,
EVMVersion _evmVersion,
+ boost::optional<Error::Type> _errorTypeForLoose,
AsmFlavour _flavour = AsmFlavour::Loose,
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
- ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), m_flavour(_flavour) {}
+ ):
+ m_resolver(_resolver),
+ m_info(_analysisInfo),
+ m_errorReporter(_errorReporter),
+ m_evmVersion(_evmVersion),
+ m_flavour(_flavour),
+ m_errorTypeForLoose(_errorTypeForLoose)
+ {}
bool analyze(assembly::Block const& _block);
@@ -91,6 +100,11 @@ private:
void expectValidType(std::string const& type, SourceLocation const& _location);
void warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location);
+ /// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler
+ /// exception (if the flavour is not Loose), reports an error/warning
+ /// (if m_errorTypeForLoose is set) or does nothing.
+ void checkLooseFeature(SourceLocation const& _location, std::string const& _description);
+
int m_stackHeight = 0;
julia::ExternalIdentifierAccess::Resolver m_resolver;
Scope* m_currentScope = nullptr;
@@ -101,6 +115,7 @@ private:
ErrorReporter& m_errorReporter;
EVMVersion m_evmVersion;
AsmFlavour m_flavour = AsmFlavour::Loose;
+ boost::optional<Error::Type> m_errorTypeForLoose;
};
}
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index 7a9fffbf..7f97336b 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -91,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann
bool AssemblyStack::analyzeParsed()
{
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
- assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, languageToAsmFlavour(m_language));
+ assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_evmVersion, boost::none, languageToAsmFlavour(m_language));
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
return m_analysisSuccessful;
}
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
index 9d02c498..0f014372 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -79,8 +79,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
scanner.lineAtPosition(_location->start) <<
endl <<
string(startColumn, ' ') <<
- "^\n" <<
- "Spanning multiple lines.\n";
+ "^ (Relevant source part starts here and spans across multiple lines)." <<
+ endl;
}
void SourceReferenceFormatter::printSourceName(SourceLocation const* _location)
diff --git a/scripts/tests.sh b/scripts/tests.sh
index 60dae2e4..bf4ee3d9 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -59,12 +59,15 @@ function download_eth()
ETH_PATH="eth"
else
mkdir -p /tmp/test
- ETH_BINARY=eth_byzantium_artful
- ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
if grep -i trusty /etc/lsb-release >/dev/null 2>&1
then
- ETH_BINARY=eth_byzantium2
- ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f"
+ # built from 1ecff3cac12f0fbbeea3e645f331d5ac026b24d3 at 2018-03-06
+ ETH_BINARY=eth_byzantium_trusty
+ ETH_HASH="5432ea81c150e8a3547615bf597cd6dce9e1e27b"
+ else
+ # built from ?? at 2018-02-13 ?
+ ETH_BINARY=eth_byzantium_artful
+ ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
fi
wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY
test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth"
@@ -121,4 +124,4 @@ wait $CMDLINE_PID
pkill "$ETH_PID" || true
sleep 4
-pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file
+pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp
index d8cd20b6..41f5d320 100644
--- a/test/libjulia/Common.cpp
+++ b/test/libjulia/Common.cpp
@@ -67,6 +67,7 @@ pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test:
*analysisInfo,
errorReporter,
dev::test::Options::get().evmVersion(),
+ boost::none,
flavour
);
if (analyzer.analyze(*parserResult))
diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp
index 6476c4d4..df905dd6 100644
--- a/test/libjulia/Parser.cpp
+++ b/test/libjulia/Parser.cpp
@@ -60,6 +60,7 @@ bool parse(string const& _source, ErrorReporter& errorReporter)
analysisInfo,
errorReporter,
dev::test::Options::get().evmVersion(),
+ boost::none,
assembly::AsmFlavour::IULIA
)).analyze(*parserResult);
}
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index c352a2c2..33cd1419 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -21,13 +21,20 @@
* Unit tests for the solidity expression compiler, testing the behaviour of the code.
*/
+#include <test/libsolidity/SolidityExecutionFramework.h>
+
+#include <test/TestHelper.h>
+
+#include <libsolidity/interface/Exceptions.h>
+#include <libsolidity/interface/EVMVersion.h>
+
+#include <libevmasm/Assembly.h>
+
+#include <boost/test/unit_test.hpp>
+
#include <functional>
#include <string>
#include <tuple>
-#include <boost/test/unit_test.hpp>
-#include <libevmasm/Assembly.h>
-#include <libsolidity/interface/Exceptions.h>
-#include <test/libsolidity/SolidityExecutionFramework.h>
using namespace std;
using namespace std::placeholders;
@@ -5352,6 +5359,18 @@ BOOST_AUTO_TEST_CASE(super_overload)
ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
}
+BOOST_AUTO_TEST_CASE(gasleft_shadow_resolution)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function gasleft() returns(uint256) { return 0; }
+ function f() returns(uint256) { return gasleft(); }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(0));
+}
+
BOOST_AUTO_TEST_CASE(bool_conversion)
{
char const* sourceCode = R"(
@@ -10766,6 +10785,51 @@ BOOST_AUTO_TEST_CASE(snark)
BOOST_CHECK(callContractFunction("verifyTx()") == encodeArgs(true));
}
+BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure)
+{
+ char const* sourceCode = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ uint x;
+ function f() public returns (uint) {
+ x = 3;
+ return 1;
+ }
+ }
+ interface CView {
+ function f() view external returns (uint);
+ }
+ interface CPure {
+ function f() pure external returns (uint);
+ }
+ contract D {
+ function f() public returns (uint) {
+ return (new C()).f();
+ }
+ function fview() public returns (uint) {
+ return (CView(new C())).f();
+ }
+ function fpure() public returns (uint) {
+ return (CPure(new C())).f();
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "D");
+ // This should work (called via CALL)
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(1));
+ if (dev::test::Options::get().evmVersion().hasStaticCall())
+ {
+ // These should throw (called via STATICCALL)
+ ABI_CHECK(callContractFunction("fview()"), encodeArgs());
+ ABI_CHECK(callContractFunction("fpure()"), encodeArgs());
+ }
+ else
+ {
+ ABI_CHECK(callContractFunction("fview()"), encodeArgs(1));
+ ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1));
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index 44d3daff..5f044b44 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -515,6 +515,39 @@ BOOST_AUTO_TEST_CASE(blockhash)
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
+BOOST_AUTO_TEST_CASE(gas_left)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() returns (uint256 val) {
+ return msg.gas;
+ }
+ }
+ )";
+ bytes code = compileFirstExpression(
+ sourceCode, {}, {},
+ {make_shared<MagicVariableDeclaration>("msg", make_shared<MagicType>(MagicType::Kind::Message))}
+ );
+
+ bytes expectation({byte(Instruction::GAS)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+
+ sourceCode = R"(
+ contract test {
+ function f() returns (uint256 val) {
+ return gasleft();
+ }
+ }
+ )";
+ code = compileFirstExpression(
+ sourceCode, {}, {},
+ {make_shared<MagicVariableDeclaration>("gasleft", make_shared<FunctionType>(strings(), strings{"uint256"}, FunctionType::Kind::GasLeft))}
+ );
+
+ expectation = bytes({byte(Instruction::GAS)});
+ BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index d4962b6c..85ca3968 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -102,7 +102,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_050)
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
"This declaration shadows an existing declaration.",
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -133,7 +132,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050)
}
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -164,7 +162,6 @@ BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050)
}
)";
CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
- "Experimental features",
"Unused local variable",
"Unused local variable"
}));
@@ -262,7 +259,7 @@ BOOST_AUTO_TEST_CASE(scoping_for)
}
}
)";
- CHECK_WARNING(text, "Experimental features");
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(scoping_for2)
@@ -276,7 +273,7 @@ BOOST_AUTO_TEST_CASE(scoping_for2)
}
}
)";
- CHECK_WARNING(text, "Experimental features");
+ CHECK_SUCCESS_NO_WARNINGS(text);
}
BOOST_AUTO_TEST_CASE(scoping_for3)
@@ -981,6 +978,62 @@ BOOST_AUTO_TEST_CASE(functions_with_stucts_of_non_external_types_in_interface_ne
CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
}
+BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays_new_abi)
+{
+ char const* text = R"(
+ pragma experimental ABIEncoderV2;
+
+ contract C {
+ function f() public pure returns (string[][]) {}
+ }
+ )";
+ CHECK_WARNING(text, "Experimental features");
+}
+
+BOOST_AUTO_TEST_CASE(returning_multi_dimensional_arrays)
+{
+ char const* text = R"(
+ contract C {
+ function f() public pure returns (string[][]) {}
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder");
+}
+
+BOOST_AUTO_TEST_CASE(returning_multi_dimensional_static_arrays)
+{
+ char const* text = R"(
+ contract C {
+ function f() public pure returns (uint[][2]) {}
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder");
+}
+
+BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_new_abi)
+{
+ char const* text = R"(
+ pragma experimental ABIEncoderV2;
+
+ contract C {
+ struct S { string[] s; }
+ function f() public pure returns (S) {}
+ }
+ )";
+ CHECK_WARNING(text, "Experimental features");
+}
+
+BOOST_AUTO_TEST_CASE(returning_arrays_in_structs_arrays)
+{
+ char const* text = R"(
+ contract C {
+ struct S { string[] s; }
+ function f() public pure returns (S x) {}
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "only supported in the new experimental ABI encoder");
+}
+
BOOST_AUTO_TEST_CASE(function_external_call_allowed_conversion)
{
char const* text = R"(
@@ -5893,10 +5946,11 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_negative_stack)
BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract c {
uint8 x;
function f() public {
- assembly { x pop }
+ assembly { pop(x) }
}
}
)";
@@ -5906,6 +5960,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_unbalanced_two_stack_load)
BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
modifier m {
uint a = 1;
@@ -5914,7 +5969,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier)
}
_;
}
- function f() m {
+ function f() public m {
}
}
)";
@@ -5924,6 +5979,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_in_modifier)
BOOST_AUTO_TEST_CASE(inline_assembly_storage)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint x = 1;
function f() public {
@@ -5939,6 +5995,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage)
BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint x = 1;
modifier m {
@@ -5947,7 +6004,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers)
}
_;
}
- function f() m {
+ function f() public m {
}
}
)";
@@ -5957,6 +6014,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_in_modifiers)
BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint constant x = 1;
function f() public {
@@ -5972,6 +6030,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_assign)
BOOST_AUTO_TEST_CASE(inline_assembly_constant_access)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint constant x = 1;
function f() public {
@@ -5987,6 +6046,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access)
BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
function f() public {
uint a;
@@ -6002,6 +6062,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions)
BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint[] r;
function f() public {
@@ -6018,6 +6079,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_stor
BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract test {
uint a;
function f() pure public {
@@ -6048,6 +6110,7 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_variable_via_offset)
BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables)
{
char const* text = R"(
+ pragma experimental "v0.5.0";
contract C {
function f(bytes bytesAsCalldata) external {
assembly {
@@ -6059,6 +6122,182 @@ BOOST_AUTO_TEST_CASE(inline_assembly_calldata_variables)
CHECK_ERROR(text, TypeError, "Call data elements cannot be accessed directly.");
}
+BOOST_AUTO_TEST_CASE(inline_assembly_050_literals_on_stack)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public {
+ assembly {
+ 1
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::SyntaxError, "are not supposed to return"},
+ {Error::Type::DeclarationError, "Unbalanced stack"},
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_literals_on_stack)
+{
+ char const* text = R"(
+ contract C {
+ function f() pure public {
+ assembly {
+ 1
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::Warning, "are not supposed to return"},
+ {Error::Type::DeclarationError, "Unbalanced stack"},
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_050_bare_instructions)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() view public {
+ assembly {
+ address
+ pop
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::SyntaxError, "The use of non-functional"},
+ {Error::Type::SyntaxError, "The use of non-functional"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_bare_instructions)
+{
+ char const* text = R"(
+ contract C {
+ function f() view public {
+ assembly {
+ address
+ pop
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::Warning, "The use of non-functional"},
+ {Error::Type::Warning, "The use of non-functional"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_050_labels)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public {
+ assembly {
+ label:
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::SyntaxError, "Jump instructions and labels are low-level"},
+ {Error::Type::SyntaxError, "The use of labels is deprecated"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_labels)
+{
+ char const* text = R"(
+ contract C {
+ function f() pure public {
+ assembly {
+ label:
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::Warning, "Jump instructions and labels are low-level"},
+ {Error::Type::Warning, "The use of labels is deprecated"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_050_jump)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public {
+ assembly {
+ jump(2)
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::SyntaxError, "Jump instructions and labels are low-level"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_jump)
+{
+ char const* text = R"(
+ contract C {
+ function f() pure public {
+ assembly {
+ jump(2)
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::TypeError, "Function declared as pure"},
+ {Error::Type::Warning, "Jump instructions and labels are low-level"}
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_050_leave_items_on_stack)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public {
+ assembly {
+ mload(0)
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::SyntaxError, "are not supposed to return"},
+ {Error::Type::DeclarationError, "Unbalanced stack"},
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(inline_assembly_leave_items_on_stack)
+{
+ char const* text = R"(
+ contract C {
+ function f() pure public {
+ assembly {
+ mload(0)
+ }
+ }
+ }
+ )";
+ CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
+ {Error::Type::Warning, "are not supposed to return"},
+ {Error::Type::DeclarationError, "Unbalanced stack"},
+ }));
+}
+
BOOST_AUTO_TEST_CASE(invalid_mobile_type)
{
char const* text = R"(
@@ -7121,6 +7360,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy_as_variable)
)";
vector<pair<Error::Type, std::string>> expectations(vector<pair<Error::Type, std::string>>{
{Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"},
+ {Error::Type::Warning, "The use of non-functional instructions is deprecated."},
{Error::Type::DeclarationError, "Unbalanced stack"}
});
if (!dev::test::Options::get().evmVersion().supportsReturndata())
@@ -7136,7 +7376,8 @@ BOOST_AUTO_TEST_CASE(create2_as_variable)
CHECK_ALLOW_MULTI(text, (std::vector<std::pair<Error::Type, std::string>>{
{Error::Type::Warning, "Variable is shadowed in inline assembly by an instruction of the same name"},
{Error::Type::Warning, "The \"create2\" instruction is not supported by the VM version"},
- {Error::Type::DeclarationError, "Unbalanced stack"}
+ {Error::Type::DeclarationError, "Unbalanced stack"},
+ {Error::Type::Warning, "not supposed to return values"}
}));
}
@@ -7430,6 +7671,50 @@ BOOST_AUTO_TEST_CASE(builtin_reject_gas)
CHECK_ERROR(text, TypeError, "Member \"gas\" not found or not visible after argument-dependent lookup");
}
+BOOST_AUTO_TEST_CASE(gasleft)
+{
+ char const* text = R"(
+ contract C {
+ function f() public view returns (uint256 val) { return msg.gas; }
+ }
+ )";
+ CHECK_WARNING(text, "\"msg.gas\" has been deprecated in favor of \"gasleft()\"");
+
+ text = R"(
+ contract C {
+ function f() public view returns (uint256 val) { return gasleft(); }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() public returns (uint256 val) { return msg.gas; }
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "\"msg.gas\" has been deprecated in favor of \"gasleft()\"");
+}
+
+BOOST_AUTO_TEST_CASE(gasleft_shadowing)
+{
+ char const* text = R"(
+ contract C {
+ function gasleft() public pure returns (bytes32 val) { return "abc"; }
+ function f() public pure returns (bytes32 val) { return gasleft(); }
+ }
+ )";
+ CHECK_WARNING(text, "This declaration shadows a builtin symbol.");
+
+ text = R"(
+ contract C {
+ uint gasleft;
+ function f() public { gasleft = 42; }
+ }
+ )";
+ CHECK_WARNING(text, "This declaration shadows a builtin symbol.");
+}
+
BOOST_AUTO_TEST_CASE(builtin_reject_value)
{
char const* text = R"(
@@ -7691,7 +7976,7 @@ BOOST_AUTO_TEST_CASE(non_external_fallback)
function () external { }
}
)";
- CHECK_WARNING(text, "Experimental features are turned on.");
+ CHECK_SUCCESS_NO_WARNINGS(text);
text = R"(
pragma experimental "v0.5.0";
contract C {
diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp
index 6e8620f9..26ff461c 100644
--- a/test/libsolidity/ViewPureChecker.cpp
+++ b/test/libsolidity/ViewPureChecker.cpp
@@ -111,6 +111,7 @@ BOOST_AUTO_TEST_CASE(environment_access)
"block.difficulty",
"block.number",
"block.gaslimit",
+ "gasleft()",
"msg.gas",
"msg.value",
"msg.sender",