aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/GlobalContext.cpp4
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp25
-rw-r--r--libsolidity/analysis/SemVerHandler.cpp39
-rw-r--r--libsolidity/analysis/TypeChecker.cpp73
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp8
-rw-r--r--libsolidity/ast/AST.cpp17
-rw-r--r--libsolidity/ast/AST.h20
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp18
-rw-r--r--libsolidity/ast/Types.cpp91
-rw-r--r--libsolidity/ast/Types.h24
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp8
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp2
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp9
-rw-r--r--libsolidity/codegen/CompilerUtils.h2
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp31
-rw-r--r--libsolidity/formal/SMTChecker.cpp2
-rw-r--r--libsolidity/formal/Z3Interface.cpp2
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp7
-rw-r--r--libsolidity/inlineasm/AsmCodeGen.h2
-rw-r--r--libsolidity/interface/CompilerStack.cpp5
-rw-r--r--libsolidity/interface/EVMVersion.h1
-rw-r--r--libsolidity/interface/Exceptions.cpp3
-rw-r--r--libsolidity/interface/GasEstimator.cpp5
-rw-r--r--libsolidity/interface/Natspec.cpp3
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp11
-rw-r--r--libsolidity/interface/StandardCompiler.cpp39
-rw-r--r--libsolidity/parsing/Parser.cpp30
-rw-r--r--libsolidity/parsing/Scanner.cpp6
-rw-r--r--libsolidity/parsing/Token.h7
29 files changed, 351 insertions, 143 deletions
diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp
index 3ac8fd47..cba2655c 100644
--- a/libsolidity/analysis/GlobalContext.cpp
+++ b/libsolidity/analysis/GlobalContext.cpp
@@ -56,10 +56,10 @@ m_magicVariables(vector<shared_ptr<MagicVariableDeclaration const>>{
make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings(), strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("revert", make_shared<FunctionType>(strings{"string memory"}, strings(), FunctionType::Kind::Revert, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("ripemd160", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes20"}, FunctionType::Kind::RIPEMD160, false, StateMutability::Pure)),
- make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
+ make_shared<MagicVariableDeclaration>("selfdestruct", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("sha256", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::SHA256, false, StateMutability::Pure)),
make_shared<MagicVariableDeclaration>("sha3", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bytes32"}, FunctionType::Kind::KECCAK256, false, StateMutability::Pure)),
- make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address"}, strings{}, FunctionType::Kind::Selfdestruct)),
+ make_shared<MagicVariableDeclaration>("suicide", make_shared<FunctionType>(strings{"address payable"}, strings{}, FunctionType::Kind::Selfdestruct)),
make_shared<MagicVariableDeclaration>("tx", make_shared<MagicType>(MagicType::Kind::Transaction))
})
{
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 8750b47b..81de3c43 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -112,7 +112,28 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
{
- _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName());
+ if (!_typeName.annotation().type)
+ {
+ _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName());
+ if (_typeName.stateMutability().is_initialized())
+ {
+ // for non-address types this was already caught by the parser
+ solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
+ switch(*_typeName.stateMutability())
+ {
+ case StateMutability::Payable:
+ case StateMutability::NonPayable:
+ _typeName.annotation().type = make_shared<AddressType>(*_typeName.stateMutability());
+ break;
+ default:
+ m_errorReporter.typeError(
+ _typeName.location(),
+ "Address types can only be payable or non-payable."
+ );
+ break;
+ }
+ }
+ }
return true;
}
@@ -225,6 +246,8 @@ void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get());
if (!lengthType || !lengthType->mobileType())
fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
+ else if (lengthType->isZero())
+ fatalTypeError(length->location(), "Array with zero length specified.");
else if (lengthType->isFractional())
fatalTypeError(length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
diff --git a/libsolidity/analysis/SemVerHandler.cpp b/libsolidity/analysis/SemVerHandler.cpp
index 42186396..29f6d5de 100644
--- a/libsolidity/analysis/SemVerHandler.cpp
+++ b/libsolidity/analysis/SemVerHandler.cpp
@@ -106,18 +106,22 @@ bool SemVerMatchExpression::MatchComponent::matches(SemVerVersion const& _versio
}
if (cmp == 0 && !_version.prerelease.empty() && didCompare)
cmp = -1;
- if (prefix == Token::Assign)
+
+ switch (prefix)
+ {
+ case Token::Assign:
return cmp == 0;
- else if (prefix == Token::LessThan)
+ case Token::LessThan:
return cmp < 0;
- else if (prefix == Token::LessThanOrEqual)
+ case Token::LessThanOrEqual:
return cmp <= 0;
- else if (prefix == Token::GreaterThan)
+ case Token::GreaterThan:
return cmp > 0;
- else if (prefix == Token::GreaterThanOrEqual)
+ case Token::GreaterThanOrEqual:
return cmp >= 0;
- else
+ default:
solAssert(false, "Invalid SemVer expression");
+ }
return false;
}
}
@@ -196,21 +200,22 @@ SemVerMatchExpression::MatchComponent SemVerMatchExpressionParser::parseMatchCom
{
SemVerMatchExpression::MatchComponent component;
Token::Value token = currentToken();
- if (
- token == Token::BitXor ||
- token == Token::BitNot ||
- token == Token::LessThan ||
- token == Token::LessThanOrEqual||
- token == Token::GreaterThan ||
- token == Token::GreaterThanOrEqual ||
- token == Token::Assign
- )
+
+ switch (token)
{
+ case Token::BitXor:
+ case Token::BitNot:
+ case Token::LessThan:
+ case Token::LessThanOrEqual:
+ case Token::GreaterThan:
+ case Token::GreaterThanOrEqual:
+ case Token::Assign:
component.prefix = token;
nextToken();
- }
- else
+ break;
+ default:
component.prefix = Token::Assign;
+ }
component.levelsPresent = 0;
while (component.levelsPresent < 3)
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 143ac109..bc040623 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -535,7 +535,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
toString(arguments.size()) +
" were provided."
);
- if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory)))
+ if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType::bytesMemory()))
m_errorReporter.typeError(
arguments.front()->location(),
"Invalid type for argument in function call. "
@@ -571,6 +571,9 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c
// data locations. Furthermore, storage can be a little dangerous and
// calldata is not really implemented anyway.
actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType);
+ // We force address payable for address types.
+ if (actualType->category() == Type::Category::Address)
+ actualType = make_shared<AddressType>(StateMutability::Payable);
solAssert(
!actualType->dataStoredIn(DataLocation::CallData) &&
!actualType->dataStoredIn(DataLocation::Storage),
@@ -1730,16 +1733,54 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
DataLocation dataLoc = DataLocation::Memory;
if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
dataLoc = argRefType->location();
- resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType);
- if (!argType->isExplicitlyConvertibleTo(*resultType))
- m_errorReporter.typeError(
- _functionCall.location(),
- "Explicit type conversion not allowed from \"" +
- argType->toString() +
- "\" to \"" +
- resultType->toString() +
- "\"."
- );
+ if (auto type = dynamic_cast<ReferenceType const*>(resultType.get()))
+ resultType = type->copyForLocation(dataLoc, type->isPointer());
+ if (argType->isExplicitlyConvertibleTo(*resultType))
+ {
+ if (auto argArrayType = dynamic_cast<ArrayType const*>(argType.get()))
+ {
+ auto resultArrayType = dynamic_cast<ArrayType const*>(resultType.get());
+ solAssert(!!resultArrayType, "");
+ solAssert(
+ argArrayType->location() != DataLocation::Storage ||
+ ((resultArrayType->isPointer() || (argArrayType->isByteArray() && resultArrayType->isByteArray())) &&
+ resultArrayType->location() == DataLocation::Storage),
+ "Invalid explicit conversion to storage type."
+ );
+ }
+ }
+ else
+ {
+ if (resultType->category() == Type::Category::Contract && argType->category() == Type::Category::Address)
+ {
+ solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), "");
+ solAssert(dynamic_cast<AddressType const*>(argType.get())->stateMutability() < StateMutability::Payable, "");
+ SecondarySourceLocation ssl;
+ if (auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get()))
+ if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration))
+ ssl.append("Did you mean to declare this variable as \"address payable\"?", variableDeclaration->location());
+ m_errorReporter.typeError(
+ _functionCall.location(), ssl,
+ "Explicit type conversion not allowed from non-payable \"address\" to \"" +
+ resultType->toString() +
+ "\", which has a payable fallback function."
+ );
+ }
+ else
+ m_errorReporter.typeError(
+ _functionCall.location(),
+ "Explicit type conversion not allowed from \"" +
+ argType->toString() +
+ "\" to \"" +
+ resultType->toString() +
+ "\"."
+ );
+ }
+ if (resultType->category() == Type::Category::Address)
+ {
+ bool payable = argType->isExplicitlyConvertibleTo(AddressType::addressPayable());
+ resultType = make_shared<AddressType>(payable ? StateMutability::Payable : StateMutability::NonPayable);
+ }
}
_functionCall.annotation().type = resultType;
_functionCall.annotation().isPure = isPure;
@@ -1998,7 +2039,7 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
if (!contract)
m_errorReporter.fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
if (contract->contractKind() == ContractDefinition::ContractKind::Interface)
- m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface.");
+ m_errorReporter.fatalTypeError(_newExpression.location(), "Cannot instantiate an interface.");
if (!contract->annotation().unimplementedFunctions.empty())
{
SecondarySourceLocation ssl;
@@ -2103,7 +2144,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
"after argument-dependent lookup in " + exprType->toString() +
(memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".");
if (exprType->category() == Type::Category::Contract)
- for (auto const& addressMember: AddressType().nativeMembers(nullptr))
+ for (auto const& addressMember: AddressType::addressPayable().nativeMembers(nullptr))
if (addressMember.name == memberName)
{
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
@@ -2354,14 +2395,14 @@ void TypeChecker::endVisit(Literal const& _literal)
if (_literal.looksLikeAddress())
{
// Assign type here if it even looks like an address. This prevents double errors for invalid addresses
- _literal.annotation().type = make_shared<AddressType>();
+ _literal.annotation().type = make_shared<AddressType>(StateMutability::Payable);
string msg;
- if (_literal.value().length() != 42) // "0x" + 40 hex digits
+ if (_literal.valueWithoutUnderscores().length() != 42) // "0x" + 40 hex digits
// looksLikeAddress enforces that it is a hex literal starting with "0x"
msg =
"This looks like an address but is not exactly 40 hex digits. It is " +
- to_string(_literal.value().length() - 2) +
+ to_string(_literal.valueWithoutUnderscores().length() - 2) +
" hex digits.";
else if (!_literal.passesAddressChecksum())
{
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 113a3177..b0cacc43 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -292,11 +292,11 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
if (_functionCall.annotation().kind != FunctionCallKind::FunctionCall)
return;
- StateMutability mut = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
+ StateMutability mutability = dynamic_cast<FunctionType const&>(*_functionCall.expression().annotation().type).stateMutability();
// We only require "nonpayable" to call a payble function.
- if (mut == StateMutability::Payable)
- mut = StateMutability::NonPayable;
- reportMutability(mut, _functionCall.location());
+ if (mutability == StateMutability::Payable)
+ mutability = StateMutability::NonPayable;
+ reportMutability(mutability, _functionCall.location());
}
bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 8e7a81a6..d9264230 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -311,8 +311,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
return make_shared<FunctionType>(*this, _internal);
case Declaration::Visibility::External:
return {};
- default:
- solAssert(false, "visibility() should return a Visibility");
}
}
else
@@ -327,8 +325,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
case Declaration::Visibility::Public:
case Declaration::Visibility::External:
return make_shared<FunctionType>(*this, _internal);
- default:
- solAssert(false, "visibility() should return a Visibility");
}
}
@@ -568,8 +564,6 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const
case Declaration::Visibility::Public:
case Declaration::Visibility::External:
return make_shared<FunctionType>(*this);
- default:
- solAssert(false, "visibility() should not return a Visibility");
}
// To make the compiler happy
@@ -639,6 +633,11 @@ IdentifierAnnotation& Identifier::annotation() const
return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
}
+ASTString Literal::valueWithoutUnderscores() const
+{
+ return boost::erase_all_copy(value(), "_");
+}
+
bool Literal::isHexNumber() const
{
if (token() != Token::Number)
@@ -654,20 +653,20 @@ bool Literal::looksLikeAddress() const
if (!isHexNumber())
return false;
- return abs(int(value().length()) - 42) <= 1;
+ return abs(int(valueWithoutUnderscores().length()) - 42) <= 1;
}
bool Literal::passesAddressChecksum() const
{
solAssert(isHexNumber(), "Expected hex number");
- return dev::passesAddressChecksum(value(), true);
+ return dev::passesAddressChecksum(valueWithoutUnderscores(), true);
}
string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.
- string address = value().substr(2);
+ string address = valueWithoutUnderscores().substr(2);
if (address.length() > 40)
return string();
address.insert(address.begin(), 40 - address.size(), '0');
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index a5cd277d..b84f9730 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -876,23 +876,31 @@ public:
};
/**
- * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings,
- * contracts, functions, etc.
+ * Any pre-defined type name represented by a single keyword (and possibly a state mutability for address types),
+ * i.e. it excludes mappings, contracts, functions, etc.
*/
class ElementaryTypeName: public TypeName
{
public:
- ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem):
- TypeName(_location), m_type(_elem)
- {}
+ ElementaryTypeName(
+ SourceLocation const& _location,
+ ElementaryTypeNameToken const& _elem,
+ boost::optional<StateMutability> _stateMutability = {}
+ ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability)
+ {
+ solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, "");
+ }
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ElementaryTypeNameToken const& typeName() const { return m_type; }
+ boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; }
+
private:
ElementaryTypeNameToken m_type;
+ boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type
};
/**
@@ -1671,6 +1679,8 @@ public:
/// @returns the non-parsed value of the literal
ASTString const& value() const { return *m_value; }
+ ASTString valueWithoutUnderscores() const;
+
SubDenomination subDenomination() const { return m_subDenomination; }
/// @returns true if this is a number with a hex prefix.
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 5189414c..4b282d85 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -396,10 +396,15 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
- setJsonNode(_node, "ElementaryTypeName", {
+ std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.typeName().toString()),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
- });
+ };
+
+ if (_node.stateMutability())
+ attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability())));
+
+ setJsonNode(_node, "ElementaryTypeName", std::move(attributes));
return false;
}
@@ -749,9 +754,9 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
return "memory";
case VariableDeclaration::Location::CallData:
return "calldata";
- default:
- solAssert(false, "Unknown declaration location.");
}
+ // To make the compiler happy
+ return {};
}
string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
@@ -764,9 +769,10 @@ string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
return "contract";
case ContractDefinition::ContractKind::Library:
return "library";
- default:
- solAssert(false, "Unknown kind of contract.");
}
+
+ // To make the compiler happy
+ return {};
}
string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index a302203b..e45fc81d 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -299,7 +299,7 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::Byte:
return make_shared<FixedBytesType>(1);
case Token::Address:
- return make_shared<AddressType>();
+ return make_shared<AddressType>(StateMutability::NonPayable);
case Token::Bool:
return make_shared<BoolType>();
case Token::Bytes:
@@ -340,6 +340,17 @@ TypePointer Type::fromElementaryTypeName(string const& _name)
}
return ref->copyForLocation(location, true);
}
+ else if (t->category() == Type::Category::Address)
+ {
+ if (nameParts.size() == 2)
+ {
+ if (nameParts[1] == "payable")
+ return make_shared<AddressType>(StateMutability::Payable);
+ else
+ solAssert(false, "Invalid state mutability for address type: " + nameParts[1]);
+ }
+ return make_shared<AddressType>(StateMutability::NonPayable);
+ }
else
{
solAssert(nameParts.size() == 1, "Storage location suffix only allowed for reference types");
@@ -439,21 +450,48 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members;
}
+AddressType::AddressType(StateMutability _stateMutability):
+ m_stateMutability(_stateMutability)
+{
+ solAssert(m_stateMutability == StateMutability::Payable || m_stateMutability == StateMutability::NonPayable, "");
+}
+
string AddressType::richIdentifier() const
{
- return "t_address";
+ if (m_stateMutability == StateMutability::Payable)
+ return "t_address_payable";
+ else
+ return "t_address";
+}
+
+bool AddressType::isImplicitlyConvertibleTo(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ AddressType const& other = dynamic_cast<AddressType const&>(_other);
+
+ return other.m_stateMutability <= m_stateMutability;
}
bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
+ if (auto const* contractType = dynamic_cast<ContractType const*>(&_convertTo))
+ return (m_stateMutability >= StateMutability::Payable) || !contractType->isPayable();
return isImplicitlyConvertibleTo(_convertTo) ||
- _convertTo.category() == Category::Contract ||
_convertTo.category() == Category::Integer ||
(_convertTo.category() == Category::FixedBytes && 160 == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8);
}
string AddressType::toString(bool) const
{
+ if (m_stateMutability == StateMutability::Payable)
+ return "address payable";
+ else
+ return "address";
+}
+
+string AddressType::canonicalName() const
+{
return "address";
}
@@ -461,7 +499,7 @@ u256 AddressType::literalValue(Literal const* _literal) const
{
solAssert(_literal, "");
solAssert(_literal->value().substr(0, 2) == "0x", "");
- return u256(_literal->value());
+ return u256(_literal->valueWithoutUnderscores());
}
TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const
@@ -479,17 +517,29 @@ TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointe
return Type::commonType(shared_from_this(), _other);
}
+bool AddressType::operator==(Type const& _other) const
+{
+ if (_other.category() != category())
+ return false;
+ AddressType const& other = dynamic_cast<AddressType const&>(_other);
+ return other.m_stateMutability == m_stateMutability;
+}
+
MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const
{
- return {
+ MemberList::MemberMap members = {
{"balance", make_shared<IntegerType>(256)},
{"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
{"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareDelegateCall, false)},
- {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
- {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
- {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
+ {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool", "bytes memory"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)}
};
+ if (m_stateMutability == StateMutability::Payable)
+ {
+ members.emplace_back(MemberList::Member{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)});
+ members.emplace_back(MemberList::Member{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)});
+ }
+ return members;
}
namespace
@@ -616,7 +666,7 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return TypePointer();
}
- auto commonType = Type::commonType(shared_from_this(), _other); //might be a integer or fixed point
+ auto commonType = Type::commonType(shared_from_this(), _other); //might be an integer or fixed point
if (!commonType)
return TypePointer();
@@ -1226,13 +1276,13 @@ u256 RationalNumberType::literalValue(Literal const*) const
else
{
auto fixed = fixedPointType();
- solAssert(fixed, "");
+ solAssert(fixed, "Rational number cannot be represented as fixed point type.");
int fractionalDigits = fixed->fractionalDigits();
shiftedValue = m_value.numerator() * boost::multiprecision::pow(bigint(10), fractionalDigits) / m_value.denominator();
}
// we ignore the literal and hope that the type was correctly determined
- solAssert(shiftedValue <= u256(-1), "Integer constant too large.");
+ solAssert(shiftedValue <= u256(-1), "Number constant too large.");
solAssert(shiftedValue >= -(bigint(1) << 255), "Number constant too small.");
if (m_value >= rational(0))
@@ -1476,7 +1526,9 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address;
+ if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
+ return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
+ return isImplicitlyConvertibleTo(_convertTo);
}
bool ContractType::isPayable() const
@@ -2074,8 +2126,11 @@ bool StructType::canBeUsedExternally(bool _inLibrary) const
// passed by value and thus the encoding does not differ, but it will disallow
// mappings.
for (auto const& var: m_struct.members())
+ {
+ solAssert(var->annotation().type, "");
if (!var->annotation().type->canBeUsedExternally(false))
return false;
+ }
}
return true;
}
@@ -2377,7 +2432,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType.get()))
{
if (arrayType->isByteArray())
- // Return byte arrays as as whole.
+ // Return byte arrays as whole.
break;
returnType = arrayType->baseType();
m_parameterNames.push_back("");
@@ -2589,8 +2644,8 @@ bool FunctionType::operator==(Type const& _other) const
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- if (m_kind == Kind::External && _convertTo.category() == Category::Address)
- return true;
+ if (m_kind == Kind::External && _convertTo == AddressType::address())
+ return true;
return _convertTo.category() == category();
}
@@ -3275,7 +3330,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{
case Kind::Block:
return MemberList::MemberMap({
- {"coinbase", make_shared<AddressType>()},
+ {"coinbase", make_shared<AddressType>(StateMutability::Payable)},
{"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)},
{"difficulty", make_shared<IntegerType>(256)},
@@ -3284,7 +3339,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
});
case Kind::Message:
return MemberList::MemberMap({
- {"sender", make_shared<AddressType>()},
+ {"sender", make_shared<AddressType>(StateMutability::Payable)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)},
@@ -3292,7 +3347,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
});
case Kind::Transaction:
return MemberList::MemberMap({
- {"origin", make_shared<AddressType>()},
+ {"origin", make_shared<AddressType>(StateMutability::Payable)},
{"gasprice", make_shared<IntegerType>(256)}
});
case Kind::ABI:
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 7ee66838..0f3373a1 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -174,7 +174,7 @@ public:
/// Will not contain any character which would be invalid as an identifier.
std::string identifier() const;
- /// More complex identifier strings use "parentheses", where $_ is interpreted as as
+ /// More complex identifier strings use "parentheses", where $_ is interpreted as
/// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that
/// appears as part of a user-supplied identifier is escaped as _$$$_.
/// @returns an escaped identifier (will not contain any parenthesis or commas)
@@ -319,17 +319,21 @@ protected:
class AddressType: public Type
{
public:
+ static AddressType& address() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::NonPayable)); return *addr; }
+ static AddressType& addressPayable() { static std::shared_ptr<AddressType> addr(std::make_shared<AddressType>(StateMutability::Payable)); return *addr; }
+
virtual Category category() const override { return Category::Address; }
- explicit AddressType()
- {
- }
+ explicit AddressType(StateMutability _stateMutability);
virtual std::string richIdentifier() const override;
+ virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual bool operator==(Type const& _other) const override;
+
virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; }
virtual unsigned storageBytes() const override { return 160 / 8; }
virtual bool isValueType() const override { return true; }
@@ -337,11 +341,17 @@ public:
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
virtual std::string toString(bool _short) const override;
+ virtual std::string canonicalName() const override;
virtual u256 literalValue(Literal const* _literal) const override;
virtual TypePointer encodingType() const override { return shared_from_this(); }
virtual TypePointer interfaceType(bool) const override { return shared_from_this(); }
+
+ StateMutability stateMutability(void) const { return m_stateMutability; }
+
+private:
+ StateMutability m_stateMutability;
};
/**
@@ -354,6 +364,7 @@ public:
{
Unsigned, Signed
};
+
virtual Category category() const override { return Category::Integer; }
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
@@ -654,6 +665,9 @@ protected:
class ArrayType: public ReferenceType
{
public:
+ static ArrayType& bytesMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory)); return *addr; }
+ static ArrayType& stringMemory() { static std::shared_ptr<ArrayType> addr(std::make_shared<ArrayType>(DataLocation::Memory, true)); return *addr; }
+
virtual Category category() const override { return Category::Array; }
/// Constructor for a byte array ("bytes") and string.
@@ -755,7 +769,7 @@ public:
{
if (isSuper())
return TypePointer{};
- return std::make_shared<AddressType>();
+ return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable);
}
virtual TypePointer interfaceType(bool _inLibrary) const override
{
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 5e5fe84a..6c27533c 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -242,8 +242,14 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break;
}
case Type::Category::Contract:
- templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)");
+ {
+ AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ?
+ StateMutability::Payable :
+ StateMutability::NonPayable
+ );
+ templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)");
break;
+ }
case Type::Category::Enum:
{
size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 2b77db8f..d33f749c 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -1108,8 +1108,6 @@ void ArrayUtils::accessIndex(ArrayType const& _arrayType, bool _doBoundsCheck) c
m_context << endTag;
break;
}
- default:
- solAssert(false, "");
}
}
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index e6ad6d9c..2bdf88e3 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -895,15 +895,6 @@ void CompilerUtils::convertType(
typeOnStack.location() == DataLocation::CallData,
"Invalid conversion to calldata type.");
break;
- default:
- solAssert(
- false,
- "Invalid type conversion " +
- _typeOnStack.toString(false) +
- " to " +
- _targetType.toString(false) +
- " requested."
- );
}
break;
}
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index ad3d7327..bd8170ad 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -260,7 +260,7 @@ public:
/// Stack post: <shifted_value>
void rightShiftNumberOnStack(unsigned _bits);
- /// Appends code that computes tha Keccak-256 hash of the topmost stack element of 32 byte type.
+ /// Appends code that computes the Keccak-256 hash of the topmost stack element of 32 byte type.
void computeHashStatic();
/// Bytes we need to the start of call data.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 45e58bd0..27440289 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -710,9 +710,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
arguments.front()->accept(*this);
// Optimization: If type is bytes or string, then do not encode,
// but directly compute keccak256 on memory.
- if (*argType == ArrayType(DataLocation::Memory) || *argType == ArrayType(DataLocation::Memory, true))
+ if (*argType == ArrayType::bytesMemory() || *argType == ArrayType::stringMemory())
{
- ArrayUtils(m_context).retrieveLength(ArrayType(DataLocation::Memory));
+ ArrayUtils(m_context).retrieveLength(ArrayType::bytesMemory());
m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD;
}
else
@@ -1086,7 +1086,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().abiDecode(targetTypes, false);
else
{
- utils().convertType(*firstArgType, ArrayType(DataLocation::Memory));
+ utils().convertType(*firstArgType, ArrayType::bytesMemory());
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
m_context << Instruction::SWAP1 << Instruction::MLOAD;
// stack now: <mem_pos> <length>
@@ -1098,8 +1098,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::GasLeft:
m_context << Instruction::GAS;
break;
- default:
- solAssert(false, "Invalid function type.");
}
}
return false;
@@ -1230,7 +1228,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else
solAssert(false, "Contract member is neither variable nor function.");
m_context << identifier;
- /// need to store store it as bytes4
+ /// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
return false;
}
@@ -1259,7 +1257,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
identifier = FunctionType(*function).externalIdentifier();
else
solAssert(false, "Contract member is neither variable nor function.");
- utils().convertType(type, AddressType(), true);
+ utils().convertType(type, type.isPayable() ? AddressType::addressPayable() : AddressType::address(), true);
m_context << identifier;
}
else
@@ -1277,15 +1275,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
utils().convertType(
*_memberAccess.expression().annotation().type,
- AddressType(),
+ AddressType::address(),
true
);
m_context << Instruction::BALANCE;
}
- else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
+ else if ((set<string>{"send", "transfer"}).count(member))
+ {
+ solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, "");
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ AddressType(StateMutability::Payable),
+ true
+ );
+ }
+ else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType(
*_memberAccess.expression().annotation().type,
- AddressType(),
+ AddressType::address(),
true
);
else
@@ -1296,7 +1303,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (member == "selector")
{
m_context << Instruction::SWAP1 << Instruction::POP;
- /// need to store store it as bytes4
+ /// need to store it as bytes4
utils().leftShiftNumberOnStack(224);
}
else
@@ -1966,7 +1973,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos));
bool existenceChecked = false;
- // Check the the target contract exists (has code) for non-low-level calls.
+ // Check the target contract exists (has code) for non-low-level calls.
if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 49c90405..19785817 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -639,8 +639,6 @@ void SMTChecker::checkCondition(
case smt::CheckResult::ERROR:
m_errorReporter.warning(_location, "Error trying to invoke SMT solver.");
break;
- default:
- solAssert(false, "");
}
m_interface->pop();
}
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index 747c9172..9a0ccf48 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -91,8 +91,6 @@ pair<CheckResult, vector<string>> Z3Interface::check(vector<Expression> const& _
case z3::check_result::unknown:
result = CheckResult::UNKNOWN;
break;
- default:
- solAssert(false, "");
}
if (result == CheckResult::SATISFIABLE && !_expressionsToEvaluate.empty())
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 9a0110cf..947b6d05 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -565,8 +565,10 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
// We assume that returndatacopy, returndatasize and staticcall are either all available
// or all not available.
solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), "");
+ // Similarly we assume bitwise shifting and create2 go together.
+ solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), "");
- if (_instr == solidity::Instruction::CREATE2)
+ if (_instr == solidity::Instruction::EXTCODEHASH)
m_errorReporter.warning(
_location,
"The \"" +
@@ -593,7 +595,8 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
else if ((
_instr == solidity::Instruction::SHL ||
_instr == solidity::Instruction::SHR ||
- _instr == solidity::Instruction::SAR
+ _instr == solidity::Instruction::SAR ||
+ _instr == solidity::Instruction::CREATE2
) && !m_evmVersion.hasBitwiseShifting())
m_errorReporter.warning(
_location,
diff --git a/libsolidity/inlineasm/AsmCodeGen.h b/libsolidity/inlineasm/AsmCodeGen.h
index a7d7ead1..277e1879 100644
--- a/libsolidity/inlineasm/AsmCodeGen.h
+++ b/libsolidity/inlineasm/AsmCodeGen.h
@@ -41,7 +41,7 @@ struct Block;
class CodeGenerator
{
public:
- /// Performs code generation and appends generated to to _assembly.
+ /// Performs code generation and appends generated to _assembly.
static void assemble(
Block const& _parsedData,
AsmAnalysisInfo& _analysisInfo,
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index e800b278..d1001c80 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -128,7 +128,7 @@ bool CompilerStack::addSource(string const& _name, string const& _content, bool
bool CompilerStack::parse()
{
//reset
- if(m_stackState != SourcesSet)
+ if (m_stackState != SourcesSet)
return false;
m_errorReporter.clear();
ASTNode::resetID();
@@ -988,8 +988,7 @@ Json::Value CompilerStack::gasEstimates(string const& _contractName) const
if (eth::AssemblyItems const* items = assemblyItems(_contractName))
{
Gas executionGas = gasEstimator.functionalEstimation(*items);
- u256 bytecodeSize(runtimeObject(_contractName).bytecode.size());
- Gas codeDepositGas = bytecodeSize * eth::GasCosts::createDataGas;
+ Gas codeDepositGas{eth::GasMeter::dataGas(runtimeObject(_contractName).bytecode, false)};
Json::Value creation(Json::objectValue);
creation["codeDepositCost"] = gasToJson(codeDepositGas);
diff --git a/libsolidity/interface/EVMVersion.h b/libsolidity/interface/EVMVersion.h
index b68e1f4e..657727ac 100644
--- a/libsolidity/interface/EVMVersion.h
+++ b/libsolidity/interface/EVMVersion.h
@@ -75,6 +75,7 @@ public:
bool supportsReturndata() const { return *this >= byzantium(); }
bool hasStaticCall() const { return *this >= byzantium(); }
bool hasBitwiseShifting() const { return *this >= constantinople(); }
+ bool hasCreate2() const { return *this >= constantinople(); }
/// Whether we have to retain the costs for the call opcode itself (false),
/// or whether we can just forward easily all remaining gas (true).
diff --git a/libsolidity/interface/Exceptions.cpp b/libsolidity/interface/Exceptions.cpp
index a837dce6..ecadd0b7 100644
--- a/libsolidity/interface/Exceptions.cpp
+++ b/libsolidity/interface/Exceptions.cpp
@@ -49,9 +49,6 @@ Error::Error(Type _type, SourceLocation const& _location, string const& _descrip
case Type::Warning:
m_typeName = "Warning";
break;
- default:
- solAssert(false, "");
- break;
}
if (!_location.isEmpty())
diff --git a/libsolidity/interface/GasEstimator.cpp b/libsolidity/interface/GasEstimator.cpp
index a532f86e..e70e23a2 100644
--- a/libsolidity/interface/GasEstimator.cpp
+++ b/libsolidity/interface/GasEstimator.cpp
@@ -160,8 +160,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
);
}
- PathGasMeter meter(_items, m_evmVersion);
- return meter.estimateMax(0, state);
+ return PathGasMeter::estimateMax(_items, m_evmVersion, 0, state);
}
GasEstimator::GasConsumption GasEstimator::functionalEstimation(
@@ -183,7 +182,7 @@ GasEstimator::GasConsumption GasEstimator::functionalEstimation(
if (parametersSize > 0)
state->feedItem(swapInstruction(parametersSize));
- return PathGasMeter(_items, m_evmVersion).estimateMax(_offset, state);
+ return PathGasMeter::estimateMax(_items, m_evmVersion, _offset, state);
}
set<ASTNode const*> GasEstimator::finestNodesAtLocation(
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index a8716862..11dde349 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -83,7 +83,8 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
doc["details"] = Json::Value(dev);
auto constructorDefinition(_contractDef.constructor());
- if (constructorDefinition) {
+ if (constructorDefinition)
+ {
Json::Value constructor(devDocumentation(constructorDefinition->annotation().docTags));
if (!constructor.empty())
// add the constructor, only if we have any documentation to add
diff --git a/libsolidity/interface/SourceReferenceFormatter.cpp b/libsolidity/interface/SourceReferenceFormatter.cpp
index 0f014372..865907e2 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -55,8 +55,15 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
}
if (line.length() > 150)
{
- line = " ... " + line.substr(startColumn, locationLength) + " ... ";
- startColumn = 5;
+ int len = line.length();
+ line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
+ if (startColumn + locationLength + 35 < len)
+ line += " ...";
+ if (startColumn > 35)
+ {
+ line = " ... " + line;
+ startColumn = 40;
+ }
endColumn = startColumn + locationLength;
}
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index c1996777..2305da13 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -280,6 +280,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
for (auto const& url: sources[sourceName]["urls"])
{
+ if (!url.isString())
+ return formatFatalError("JSONError", "URL must be a string.");
ReadCallback::Result result = m_readFile(url.asString());
if (result.success)
{
@@ -320,7 +322,9 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
if (settings.isMember("evmVersion"))
{
- boost::optional<EVMVersion> version = EVMVersion::fromString(settings.get("evmVersion", {}).asString());
+ if (!settings["evmVersion"].isString())
+ return formatFatalError("JSONError", "evmVersion must be a string.");
+ boost::optional<EVMVersion> version = EVMVersion::fromString(settings["evmVersion"].asString());
if (!version)
return formatFatalError("JSONError", "Invalid EVM version requested.");
m_compilerStack.setEVMVersion(*version);
@@ -329,6 +333,8 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
vector<CompilerStack::Remapping> remappings;
for (auto const& remapping: settings.get("remappings", Json::Value()))
{
+ if (!remapping.isString())
+ return formatFatalError("JSONError", "Remapping entry must be a string.");
if (auto r = CompilerStack::parseRemapping(remapping.asString()))
remappings.emplace_back(std::move(*r));
else
@@ -336,10 +342,25 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
}
m_compilerStack.setRemappings(remappings);
- Json::Value optimizerSettings = settings.get("optimizer", Json::Value());
- bool const optimize = optimizerSettings.get("enabled", Json::Value(false)).asBool();
- unsigned const optimizeRuns = optimizerSettings.get("runs", Json::Value(200u)).asUInt();
- m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
+ if (settings.isMember("optimizer"))
+ {
+ Json::Value optimizerSettings = settings["optimizer"];
+ if (optimizerSettings.isMember("enabled"))
+ {
+ if (!optimizerSettings["enabled"].isBool())
+ return formatFatalError("JSONError", "The \"enabled\" setting must be a boolean.");
+
+ bool const optimize = optimizerSettings["enabled"].asBool();
+ unsigned optimizeRuns = 200;
+ if (optimizerSettings.isMember("runs"))
+ {
+ if (!optimizerSettings["runs"].isUInt())
+ return formatFatalError("JSONError", "The \"runs\" setting must be an unsigned number.");
+ optimizeRuns = optimizerSettings["runs"].asUInt();
+ }
+ m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
+ }
+ }
map<string, h160> libraries;
Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
@@ -349,9 +370,11 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
{
auto const& jsonSourceName = jsonLibraries[sourceName];
if (!jsonSourceName.isObject())
- return formatFatalError("JSONError", "library entry is not a JSON object.");
+ return formatFatalError("JSONError", "Library entry is not a JSON object.");
for (auto const& library: jsonSourceName.getMemberNames())
{
+ if (!jsonSourceName[library].isString())
+ return formatFatalError("JSONError", "Library address must be a string.");
string address = jsonSourceName[library].asString();
if (!boost::starts_with(address, "0x"))
@@ -607,7 +630,7 @@ string StandardCompiler::compile(string const& _input) noexcept
}
catch (...)
{
- return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
+ return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error parsing input JSON.\"}]}";
}
// cout << "Input: " << input.toStyledString() << endl;
@@ -620,6 +643,6 @@ string StandardCompiler::compile(string const& _input) noexcept
}
catch (...)
{
- return "{\"errors\":\"[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
+ return "{\"errors\":[{\"type\":\"JSONError\",\"component\":\"general\",\"severity\":\"error\",\"message\":\"Error writing output JSON.\"}]}";
}
}
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 0c41e332..1228b833 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -57,7 +57,7 @@ public:
solAssert(m_location.sourceName, "");
if (m_location.end < 0)
markEndPosition();
- return make_shared<NodeType>(m_location, forward<Args>(_args)...);
+ return make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
}
private:
@@ -813,8 +813,24 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
unsigned secondSize;
tie(firstSize, secondSize) = m_scanner->currentTokenInfo();
ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize);
- type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName);
+ ASTNodeFactory nodeFactory(*this);
+ nodeFactory.markEndPosition();
m_scanner->next();
+ auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable);
+ if (Token::isStateMutabilitySpecifier(m_scanner->currentToken(), false))
+ {
+ if (elemTypeName.token() == Token::Address)
+ {
+ nodeFactory.markEndPosition();
+ stateMutability = parseStateMutability();
+ }
+ else
+ {
+ parserError("State mutability can only be specified for address types.");
+ m_scanner->next();
+ }
+ }
+ type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability);
}
else if (token == Token::Var)
{
@@ -1615,8 +1631,8 @@ Parser::LookAheadInfo Parser::peekStatementType() const
// Distinguish between variable declaration (and potentially assignment) and expression statement
// (which include assignments to other expressions and pre-declared variables).
// We have a variable declaration if we get a keyword that specifies a type name.
- // If it is an identifier or an elementary type name followed by an identifier, we also have
- // a variable declaration.
+ // If it is an identifier or an elementary type name followed by an identifier
+ // or a mutability specifier, we also have a variable declaration.
// If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;").
// In all other cases, we have an expression statement.
Token::Value token(m_scanner->currentToken());
@@ -1627,6 +1643,12 @@ Parser::LookAheadInfo Parser::peekStatementType() const
if (mightBeTypeName)
{
Token::Value next = m_scanner->peekNextToken();
+ // So far we only allow ``address payable`` in variable declaration statements and in no other
+ // kind of statement. This means, for example, that we do not allow type expressions of the form
+ // ``address payable;``.
+ // If we want to change this in the future, we need to consider another scanner token here.
+ if (Token::isElementaryTypeName(token) && Token::isStateMutabilitySpecifier(next, false))
+ return LookAheadInfo::VariableDeclaration;
if (next == Token::Identifier || Token::isLocationSpecifier(next))
return LookAheadInfo::VariableDeclaration;
if (next == Token::LBrack || next == Token::Period)
diff --git a/libsolidity/parsing/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
index c9d5b969..9a7f85cb 100644
--- a/libsolidity/parsing/Scanner.cpp
+++ b/libsolidity/parsing/Scanner.cpp
@@ -601,7 +601,7 @@ void Scanner::scanToken()
{
tie(token, m, n) = scanIdentifierOrKeyword();
- // Special case for hexademical literals
+ // Special case for hexadecimal literals
if (token == Token::Hex)
{
// reset
@@ -780,13 +780,13 @@ Token::Value Scanner::scanNumber(char _charSeen)
{
addLiteralCharAndAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx or a hex number
- if (m_char == 'x' || m_char == 'X')
+ if (m_char == 'x')
{
// hex number
kind = HEX;
addLiteralCharAndAdvance();
if (!isHexDigit(m_char))
- return Token::Illegal; // we must have at least one hex digit after 'x'/'X'
+ return Token::Illegal; // we must have at least one hex digit after 'x'
while (isHexDigit(m_char) || m_char == '_') // We keep the underscores for later validation
addLiteralCharAndAdvance();
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index 7ce24e69..73c85482 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -312,7 +312,12 @@ public:
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage || op == CallData; }
- static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; }
+ static bool isStateMutabilitySpecifier(Value op, bool _allowConstant = true)
+ {
+ if (op == Constant && _allowConstant)
+ return true;
+ return op == Pure || op == View || op == Payable;
+ }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; }
static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; }
static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= Unchecked); }