aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/GlobalContext.cpp2
-rw-r--r--libsolidity/analysis/SemVerHandler.h6
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp19
-rw-r--r--libsolidity/analysis/TypeChecker.cpp34
-rw-r--r--libsolidity/ast/AST.cpp6
-rw-r--r--libsolidity/ast/AST.h2
-rw-r--r--libsolidity/ast/Types.cpp66
-rw-r--r--libsolidity/ast/Types.h11
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp3
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp2
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp50
-rw-r--r--libsolidity/codegen/ExpressionCompiler.h2
-rw-r--r--libsolidity/formal/Why3Translator.cpp14
-rw-r--r--libsolidity/formal/Why3Translator.h1
-rw-r--r--libsolidity/grammar.txt4
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.cpp26
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp14
-rw-r--r--libsolidity/interface/CompilerStack.cpp20
-rw-r--r--libsolidity/interface/GasEstimator.cpp2
19 files changed, 230 insertions, 54 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index a7ffcfad..d075949e 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -48,6 +48,8 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{make_shared<
make_shared<FunctionType>(strings{"uint256", "uint256", "uint256"}, strings{"uint256"}, FunctionType::Location::MulMod)),
make_shared<MagicVariableDeclaration>("sha3",
make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
+ make_shared<MagicVariableDeclaration>("keccak256",
+ make_shared<FunctionType>(strings(), strings{"bytes32"}, FunctionType::Location::SHA3, true)),
make_shared<MagicVariableDeclaration>("log0",
make_shared<FunctionType>(strings{"bytes32"}, strings{}, FunctionType::Location::Log0)),
make_shared<MagicVariableDeclaration>("log1",
diff --git a/libsolidity/analysis/SemVerHandler.h b/libsolidity/analysis/SemVerHandler.h
index 3c110b19..e3b642db 100644
--- a/libsolidity/analysis/SemVerHandler.h
+++ b/libsolidity/analysis/SemVerHandler.h
@@ -40,6 +40,12 @@ struct SemVerVersion
std::string prerelease;
std::string build;
+ unsigned major() const { return numbers[0]; }
+ unsigned minor() const { return numbers[1]; }
+ unsigned patch() const { return numbers[2]; }
+
+ bool isPrerelease() const { return !prerelease.empty(); }
+
explicit SemVerVersion(std::string const& _versionString = "0.0.0");
};
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index a95b4879..dbaa15ed 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -52,13 +52,22 @@ void SyntaxChecker::endVisit(SourceUnit const& _sourceUnit)
{
if (!m_versionPragmaFound)
{
+ string errorString("Source file does not specify required compiler version!");
+ SemVerVersion recommendedVersion{string(VersionString)};
+ if (!recommendedVersion.isPrerelease())
+ errorString +=
+ "Consider adding \"pragma solidity ^" +
+ to_string(recommendedVersion.major()) +
+ string(".") +
+ to_string(recommendedVersion.minor()) +
+ string(".") +
+ to_string(recommendedVersion.patch());
+ string(";\"");
+
auto err = make_shared<Error>(Error::Type::Warning);
*err <<
errinfo_sourceLocation(_sourceUnit.location()) <<
- errinfo_comment(
- string("Source file does not specify required compiler version! ") +
- string("Consider adding \"pragma solidity ^") + VersionNumber + string(";\".")
- );
+ errinfo_comment(errorString);
m_errors.push_back(err);
}
}
@@ -67,7 +76,7 @@ bool SyntaxChecker::visit(PragmaDirective const& _pragma)
{
solAssert(!_pragma.tokens().empty(), "");
solAssert(_pragma.tokens().size() == _pragma.literals().size(), "");
- if (_pragma.tokens()[0] != Token::Identifier && _pragma.literals()[0] != "solidity")
+ if (_pragma.tokens()[0] != Token::Identifier || _pragma.literals()[0] != "solidity")
syntaxError(_pragma.location(), "Unknown pragma \"" + _pragma.literals()[0] + "\"");
else
{
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index ae7c13c8..46f4f7f6 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -609,6 +609,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
return false;
pushes = 1;
}
+ else
+ return false;
for (unsigned i = 0; i < pushes; ++i)
_assembly.append(u256(0)); // just to verify the stack height
}
@@ -716,11 +718,10 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
if (ref->dataStoredIn(DataLocation::Storage))
{
- auto err = make_shared<Error>(Error::Type::Warning);
- *err <<
- errinfo_sourceLocation(varDecl.location()) <<
- errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?");
- m_errors.push_back(err);
+ warning(
+ varDecl.location(),
+ "Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?"
+ );
}
}
varDecl.accept(*this);
@@ -879,6 +880,10 @@ bool TypeChecker::visit(Conditional const& _conditional)
TypePointer trueType = type(_conditional.trueExpression())->mobileType();
TypePointer falseType = type(_conditional.falseExpression())->mobileType();
+ if (!trueType)
+ fatalTypeError(_conditional.trueExpression().location(), "Invalid mobile type.");
+ if (!falseType)
+ fatalTypeError(_conditional.falseExpression().location(), "Invalid mobile type.");
TypePointer commonType = Type::commonType(trueType, falseType);
if (!commonType)
@@ -985,10 +990,16 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
types.push_back(type(*components[i]));
if (_tuple.isInlineArray())
solAssert(!!types[i], "Inline array cannot have empty components");
- if (i == 0 && _tuple.isInlineArray())
- inlineArrayType = types[i]->mobileType();
- else if (_tuple.isInlineArray() && inlineArrayType)
- inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
+ if (_tuple.isInlineArray())
+ {
+ if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
+ fatalTypeError(components[i]->location(), "Invalid mobile type.");
+
+ if (i == 0)
+ inlineArrayType = types[i]->mobileType();
+ else if (inlineArrayType)
+ inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
+ }
}
else
types.push_back(TypePointer());
@@ -1375,6 +1386,11 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
}
else if (exprType->category() == Type::Category::FixedBytes)
annotation.isLValue = false;
+ else if (TypeType const* typeType = dynamic_cast<decltype(typeType)>(exprType.get()))
+ {
+ if (ContractType const* contractType = dynamic_cast<decltype(contractType)>(typeType->actualType().get()))
+ annotation.isLValue = annotation.referencedDeclaration->isLValue();
+ }
return false;
}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 294daa13..695d9881 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -151,7 +151,7 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::inter
if (signaturesSeen.count(functionSignature) == 0)
{
signaturesSeen.insert(functionSignature);
- FixedHash<4> hash(dev::sha3(functionSignature));
+ FixedHash<4> hash(dev::keccak256(functionSignature));
m_interfaceFunctionList->push_back(make_pair(hash, fun));
}
}
@@ -189,6 +189,7 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
m_inheritableMembers.reset(new vector<Declaration const*>());
auto addInheritableMember = [&](Declaration const* _decl)
{
+ solAssert(_decl, "addInheritableMember got a nullpointer.");
if (memberSeen.count(_decl->name()) == 0 && _decl->isVisibleInDerivedContracts())
{
memberSeen.insert(_decl->name());
@@ -204,6 +205,9 @@ vector<Declaration const*> const& ContractDefinition::inheritableMembers() const
for (StructDefinition const* s: definedStructs())
addInheritableMember(s);
+
+ for (EnumDefinition const* e: definedEnums())
+ addInheritableMember(e);
}
return *m_inheritableMembers;
}
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 8fd1584d..7ed4ddce 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -623,7 +623,7 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
- bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); }
+ bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); }
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 4b5f12ce..7cfed3c8 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -23,6 +23,7 @@
#include <libsolidity/ast/Types.h>
#include <limits>
#include <boost/range/adaptor/reversed.hpp>
+#include <boost/range/adaptor/sliced.hpp>
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
#include <libdevcore/SHA3.h>
@@ -197,7 +198,9 @@ TypePointer Type::forLiteral(Literal const& _literal)
TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b)
{
- if (_b->isImplicitlyConvertibleTo(*_a))
+ if (!_a || !_b)
+ return TypePointer();
+ else if (_b->isImplicitlyConvertibleTo(*_a))
return _a;
else if (_a->isImplicitlyConvertibleTo(*_b))
return _b;
@@ -241,7 +244,8 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
seenFunctions.insert(function);
FunctionType funType(*function, false);
if (auto fun = funType.asMemberFunction(true, true))
- members.push_back(MemberList::Member(function->name(), fun, function));
+ if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
+ members.push_back(MemberList::Member(function->name(), fun, function));
}
}
return members;
@@ -481,7 +485,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
!all_of(radixPoint + 1, _literal.value().end(), ::isdigit) ||
!all_of(_literal.value().begin(), radixPoint, ::isdigit)
)
- throw;
+ return make_tuple(false, rational(0));
//Only decimal notation allowed here, leading zeros would switch to octal.
auto fractionalBegin = find_if_not(
radixPoint + 1,
@@ -574,7 +578,11 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
if (!isFractional())
- return fixedBytes.numBytes() * 8 >= integerType()->numBits();
+ {
+ if (integerType())
+ return fixedBytes.numBytes() * 8 >= integerType()->numBits();
+ return false;
+ }
else
return false;
}
@@ -700,6 +708,34 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
value = rational(denominator, numerator);
break;
}
+ case Token::SHL:
+ {
+ using boost::multiprecision::pow;
+ if (fractional)
+ return TypePointer();
+ else if (other.m_value < 0)
+ return TypePointer();
+ else if (other.m_value > numeric_limits<uint32_t>::max())
+ return TypePointer();
+ uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
+ value = m_value.numerator() * pow(bigint(2), exponent);
+ break;
+ }
+ // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue
+ // determines the resulting type and the type of shift (SAR or SHR).
+ case Token::SAR:
+ {
+ using boost::multiprecision::pow;
+ if (fractional)
+ return TypePointer();
+ else if (other.m_value < 0)
+ return TypePointer();
+ else if (other.m_value > numeric_limits<uint32_t>::max())
+ return TypePointer();
+ uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
+ value = rational(m_value.numerator() / pow(bigint(2), exponent), 1);
+ break;
+ }
default:
return TypePointer();
}
@@ -1291,7 +1327,10 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) con
if (m_super)
{
// add the most derived of all functions which are visible in derived contracts
- for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
+ auto bases = m_contract.annotation().linearizedBaseContracts;
+ solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract.");
+ // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`.
+ for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size()))
for (FunctionDefinition const* function: base->definedFunctions())
{
if (!function->isVisibleInDerivedContracts())
@@ -1624,7 +1663,17 @@ TypePointer TupleType::mobileType() const
{
TypePointers mobiles;
for (auto const& c: components())
- mobiles.push_back(c ? c->mobileType() : TypePointer());
+ {
+ if (c)
+ {
+ auto mt = c->mobileType();
+ if (!mt)
+ return TypePointer();
+ mobiles.push_back(mt);
+ }
+ else
+ mobiles.push_back(TypePointer());
+ }
return make_shared<TupleType>(mobiles);
}
@@ -2033,7 +2082,7 @@ string FunctionType::externalSignature() const
u256 FunctionType::externalIdentifier() const
{
- return FixedHash<4>::Arith(FixedHash<4>(dev::sha3(externalSignature())));
+ return FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(externalSignature())));
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -2065,6 +2114,9 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const
{
+ if (_bound && m_parameterTypes.empty())
+ return FunctionTypePointer();
+
TypePointers parameterTypes;
for (auto const& t: m_parameterTypes)
{
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 9173f39a..3f94d11a 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -623,6 +623,7 @@ public:
}
virtual unsigned storageBytes() const override { return 20; }
virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual unsigned sizeOnStack() const override { return m_super ? 0 : 1; }
virtual bool isValueType() const override { return true; }
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName(bool _addDataLocation) const override;
@@ -871,7 +872,12 @@ public:
m_isConstant(_isConstant),
m_isPayable(_isPayable),
m_declaration(_declaration)
- {}
+ {
+ solAssert(
+ !m_bound || !m_parameterTypes.empty(),
+ "Attempted construction of bound function without self type"
+ );
+ }
TypePointers parameterTypes() const;
std::vector<std::string> parameterNames() const;
@@ -939,8 +945,9 @@ public:
/// removed and the location of reference types is changed from CallData to Memory.
/// This is needed if external functions are called on other contracts, as they cannot return
/// dynamic values.
+ /// Returns empty shared pointer on a failure. Namely, if a bound function has no parameters.
/// @param _inLibrary if true, uses DelegateCall as location.
- /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`.
+ /// @param _bound if true, the arguments are placed as `arg1.functionName(arg2, ..., argn)`.
FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const;
private:
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index ec496df8..e064c1a6 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -368,8 +368,11 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL;
}
else if (targetTypeCategory == Type::Category::Enum)
+ {
+ solAssert(_typeOnStack.mobileType(), "");
// just clean
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
+ }
else if (targetTypeCategory == Type::Category::FixedPoint)
{
solAssert(
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 18b42fce..ebb84784 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -575,7 +575,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly)
return true;
}
);
- solAssert(errors.empty(), "Code generation for inline assembly with errors requested.");
+ solAssert(Error::containsOnlyWarnings(errors), "Code generation for inline assembly with errors requested.");
m_context.setStackOffset(startStackHeight);
return false;
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 26acd8a4..9a096e2d 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -56,8 +56,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage))
{
// reference type, only convert value to mobile type and do final conversion in storeValue.
- utils().convertType(*type, *type->mobileType());
- type = type->mobileType();
+ auto mt = type->mobileType();
+ solAssert(mt, "");
+ utils().convertType(*type, *mt);
+ type = mt;
}
else
{
@@ -670,7 +672,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
if (!event.isAnonymous())
{
- m_context << u256(h256::Arith(dev::sha3(function.externalSignature())));
+ m_context << u256(h256::Arith(dev::keccak256(function.externalSignature())));
++numIndexed;
}
solAssert(numIndexed <= 4, "Too many indexed arguments.");
@@ -861,11 +863,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
}
// Special processing for TypeType because we do not want to visit the library itself
- // for internal functions.
+ // for internal functions, or enum/struct definitions.
if (TypeType const* type = dynamic_cast<TypeType const*>(_memberAccess.expression().annotation().type.get()))
{
if (dynamic_cast<ContractType const*>(type->actualType().get()))
{
+ solAssert(_memberAccess.annotation().type, "_memberAccess has no type");
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
{
if (funType->location() != FunctionType::Location::Internal)
@@ -883,6 +886,12 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
m_context << m_context.functionEntryLabel(*function).pushTag();
}
}
+ else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type.get()))
+ {
+ // no-op
+ }
+ else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
+ appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
else
_memberAccess.expression().accept(*this);
}
@@ -1196,15 +1205,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
m_context << m_context.virtualFunctionEntryLabel(*functionDef).pushTag();
else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration))
- {
- if (!variable->isConstant())
- setLValueFromDeclaration(*declaration, _identifier);
- else
- {
- variable->value()->accept(*this);
- utils().convertType(*variable->value()->annotation().type, *variable->annotation().type);
- }
- }
+ appendVariable(*variable, static_cast<Expression const&>(_identifier));
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{
if (contract->isLibrary())
@@ -1432,11 +1433,17 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Evaluate arguments.
TypePointers argumentTypes;
TypePointers parameterTypes = _functionType.parameterTypes();
- bool manualFunctionId =
+ bool manualFunctionId = false;
+ if (
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode || funKind == FunctionKind::BareDelegateCall) &&
- !_arguments.empty() &&
- _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
+ !_arguments.empty()
+ )
+ {
+ solAssert(_arguments.front()->annotation().type->mobileType(), "");
+ manualFunctionId =
+ _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
CompilerUtils::dataStartOffset;
+ }
if (manualFunctionId)
{
// If we have a Bare* and the first type has exactly 4 bytes, use it as
@@ -1633,6 +1640,17 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
utils().storeInMemoryDynamic(_expectedType);
}
+void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression)
+{
+ if (!_variable.isConstant())
+ setLValueFromDeclaration(_variable, _expression);
+ else
+ {
+ _variable.value()->accept(*this);
+ utils().convertType(*_variable.value()->annotation().type, *_variable.annotation().type);
+ }
+}
+
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
{
if (m_context.isLocalVariable(&_declaration))
diff --git a/libsolidity/codegen/ExpressionCompiler.h b/libsolidity/codegen/ExpressionCompiler.h
index 43a92a10..f4ce1fec 100644
--- a/libsolidity/codegen/ExpressionCompiler.h
+++ b/libsolidity/codegen/ExpressionCompiler.h
@@ -103,6 +103,8 @@ private:
/// expected to be on the stack and is updated by this call.
void appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression);
+ /// Appends code for a variable that might be a constant or not
+ void appendVariable(VariableDeclaration const& _variable, Expression const& _expression);
/// Sets the current LValue to a new one (of the appropriate type) from the given declaration.
/// Also retrieves the value if it was not requested by @a _expression.
void setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression);
diff --git a/libsolidity/formal/Why3Translator.cpp b/libsolidity/formal/Why3Translator.cpp
index f3831b40..813fa3ab 100644
--- a/libsolidity/formal/Why3Translator.cpp
+++ b/libsolidity/formal/Why3Translator.cpp
@@ -757,6 +757,20 @@ bool Why3Translator::visit(Literal const& _literal)
return false;
}
+bool Why3Translator::visit(PragmaDirective const& _pragma)
+{
+ if (_pragma.tokens().empty())
+ error(_pragma, "Not supported");
+ else if (_pragma.literals().empty())
+ error(_pragma, "Not supported");
+ else if (_pragma.literals()[0] != "solidity")
+ error(_pragma, "Not supported");
+ else if (_pragma.tokens()[0] != Token::Identifier)
+ error(_pragma, "A literal 'solidity' is not an identifier. Strange");
+
+ return false;
+}
+
bool Why3Translator::isStateVariable(VariableDeclaration const* _var) const
{
return contains(m_currentContract.stateVariables, _var);
diff --git a/libsolidity/formal/Why3Translator.h b/libsolidity/formal/Why3Translator.h
index 22bfff89..4fdac385 100644
--- a/libsolidity/formal/Why3Translator.h
+++ b/libsolidity/formal/Why3Translator.h
@@ -94,6 +94,7 @@ private:
virtual bool visit(IndexAccess const& _node) override;
virtual bool visit(Identifier const& _node) override;
virtual bool visit(Literal const& _node) override;
+ virtual bool visit(PragmaDirective const& _node) override;
virtual bool visitNode(ASTNode const& _node) override
{
diff --git a/libsolidity/grammar.txt b/libsolidity/grammar.txt
index 755cf281..d84ee10c 100644
--- a/libsolidity/grammar.txt
+++ b/libsolidity/grammar.txt
@@ -77,7 +77,7 @@ Expression =
| Expression? (',' Expression)
| PrimaryExpression
-PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | StringLiteral
+PrimaryExpression = Identifier | BooleanLiteral | NumberLiteral | HexLiteral | StringLiteral
FunctionCall = ( PrimaryExpression | NewExpression | TypeName ) ( ( '.' Identifier ) | ( '[' Expression ']' ) )* '(' Expression? ( ',' Expression )* ')'
NewExpression = 'new' Identifier
@@ -88,8 +88,8 @@ BooleanLiteral = 'true' | 'false'
NumberLiteral = '0x'? [0-9]+ (' ' NumberUnit)?
NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether'
| 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years'
+HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'')
StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"'
-
Identifier = [a-zA-Z_] [a-zA-Z_0-9]*
ElementaryTypeName = 'address' | 'bool' | 'string' | 'var'
diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp
index 53d19b0a..5d920cb7 100644
--- a/libsolidity/inlineasm/AsmCodeGen.cpp
+++ b/libsolidity/inlineasm/AsmCodeGen.cpp
@@ -23,6 +23,7 @@
#include <libsolidity/inlineasm/AsmCodeGen.h>
#include <memory>
#include <functional>
+#include <libdevcore/CommonIO.h>
#include <libevmasm/Assembly.h>
#include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h>
@@ -213,10 +214,31 @@ public:
void operator()(assembly::Block const& _block)
{
size_t numVariables = m_state.variables.size();
+ int deposit = m_state.assembly.deposit();
std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this));
- // pop variables
- // we deliberately do not check stack height
+ deposit = m_state.assembly.deposit() - deposit;
+
m_state.assembly.setSourceLocation(_block.location);
+
+ // issue warnings for stack height discrepancies
+ if (deposit < 0)
+ {
+ m_state.addError(
+ Error::Type::Warning,
+ "Inline assembly block is not balanced. It takes " + toString(-deposit) + " item(s) from the stack.",
+ _block.location
+ );
+ }
+ else if (deposit > 0)
+ {
+ m_state.addError(
+ Error::Type::Warning,
+ "Inline assembly block is not balanced. It leaves " + toString(deposit) + " item(s) on the stack.",
+ _block.location
+ );
+ }
+
+ // pop variables
while (m_state.variables.size() > numVariables)
{
m_state.assembly.append(solidity::Instruction::POP);
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 5c7163ee..8d2c2ed4 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -95,7 +95,9 @@ assembly::Statement Parser::parseStatement()
fatalParserError("Label name / variable name must precede \":\".");
assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
m_scanner->next();
- if (m_scanner->currentToken() == Token::Assign)
+ // identifier:=: should be parsed as identifier: =: (i.e. a label),
+ // while identifier:= (being followed by a non-colon) as identifier := (assignment).
+ if (m_scanner->currentToken() == Token::Assign && m_scanner->peekNextToken() != Token::Colon)
{
// functional assignment
FunctionalAssignment funAss = createWithLocation<FunctionalAssignment>(identifier.location);
@@ -133,6 +135,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
// Allowed instructions, lowercase names.
static map<string, dev::solidity::Instruction> s_instructions;
if (s_instructions.empty())
+ {
for (auto const& instruction: solidity::c_instructions)
{
if (
@@ -141,24 +144,29 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
)
continue;
string name = instruction.first;
- if (instruction.second == solidity::Instruction::SUICIDE)
- name = "selfdestruct";
transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); });
s_instructions[name] = instruction.second;
}
+ // add alias for selfdestruct
+ s_instructions["selfdestruct"] = solidity::Instruction::SUICIDE;
+ }
+
Statement ret;
switch (m_scanner->currentToken())
{
case Token::Identifier:
case Token::Return:
case Token::Byte:
+ case Token::Address:
{
string literal;
if (m_scanner->currentToken() == Token::Return)
literal = "return";
else if (m_scanner->currentToken() == Token::Byte)
literal = "byte";
+ else if (m_scanner->currentToken() == Token::Address)
+ literal = "address";
else
literal = m_scanner->currentLiteral();
// first search the set of instructions.
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index ec6b5d2e..efbbd237 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -21,8 +21,10 @@
* Full-stack compiler that converts a source code string to bytecode.
*/
-#include <boost/algorithm/string.hpp>
-#include <boost/filesystem.hpp>
+#include <libsolidity/interface/CompilerStack.h>
+
+#include <libsolidity/interface/Version.h>
+#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/Scanner.h>
#include <libsolidity/parsing/Parser.h>
@@ -32,12 +34,15 @@
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/SyntaxChecker.h>
#include <libsolidity/codegen/Compiler.h>
-#include <libsolidity/interface/CompilerStack.h>
#include <libsolidity/interface/InterfaceHandler.h>
#include <libsolidity/formal/Why3Translator.h>
#include <libdevcore/SHA3.h>
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+
+
using namespace std;
using namespace dev;
using namespace dev::solidity;
@@ -100,6 +105,13 @@ bool CompilerStack::parse()
m_errors.clear();
m_parseSuccessful = false;
+ if (SemVerVersion{string(VersionString)}.isPrerelease())
+ {
+ auto err = make_shared<Error>(Error::Type::Warning);
+ *err << errinfo_comment("This is a pre-release compiler version, please do not use it in production.");
+ m_errors.push_back(err);
+ }
+
vector<string> sourcesToParse;
for (auto const& s: m_sources)
sourcesToParse.push_back(s.first);
@@ -302,7 +314,7 @@ dev::h256 CompilerStack::contractCodeHash(string const& _contractName) const
if (obj.bytecode.empty() || !obj.linkReferences.empty())
return dev::h256();
else
- return dev::sha3(obj.bytecode);
+ return dev::keccak256(obj.bytecode);
}
Json::Value CompilerStack::streamAssembly(ostream& _outStream, string const& _contractName, StringMap _sourceCodes, bool _inJsonFormat) const
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index 99ed75bc..1c804b78 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -136,7 +136,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
ExpressionClasses& classes = state->expressionClasses();
using Id = ExpressionClasses::Id;
using Ids = vector<Id>;
- Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::sha3(_signature)))));
+ Id hashValue = classes.find(u256(FixedHash<4>::Arith(FixedHash<4>(dev::keccak256(_signature)))));
Id calldata = classes.find(Instruction::CALLDATALOAD, Ids{classes.find(u256(0))});
classes.forceEqual(hashValue, Instruction::DIV, Ids{
calldata,