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.cpp20
-rw-r--r--libsolidity/analysis/SemVerHandler.cpp39
-rw-r--r--libsolidity/analysis/TypeChecker.cpp69
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp9
-rw-r--r--libsolidity/ast/Types.cpp89
-rw-r--r--libsolidity/ast/Types.h24
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp8
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp29
-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/GasEstimator.cpp5
-rw-r--r--libsolidity/interface/Natspec.cpp3
-rw-r--r--libsolidity/interface/SourceReferenceFormatter.cpp3
-rw-r--r--libsolidity/interface/StandardCompiler.cpp39
-rw-r--r--libsolidity/parsing/Scanner.cpp4
18 files changed, 266 insertions, 94 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 4b678c3b..81de3c43 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -119,11 +119,19 @@ bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
{
// for non-address types this was already caught by the parser
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
- if (!(
- *_typeName.stateMutability() == StateMutability::Payable ||
- *_typeName.stateMutability() == StateMutability::NonPayable
- ))
- m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable.");
+ 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;
@@ -238,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..3d119c82 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,7 +2395,7 @@ 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
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 4d41376a..cadc5f28 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -394,10 +394,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;
}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index a302203b..fd72bf41 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";
}
@@ -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/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 23faeef5..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>
@@ -1228,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;
}
@@ -1257,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
@@ -1275,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
@@ -1294,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
@@ -1964,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/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/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 4724fc7f..865907e2 100644
--- a/libsolidity/interface/SourceReferenceFormatter.cpp
+++ b/libsolidity/interface/SourceReferenceFormatter.cpp
@@ -59,7 +59,8 @@ void SourceReferenceFormatter::printSourceLocation(SourceLocation const* _locati
line = line.substr(max(0, startColumn - 35), min(startColumn, 35) + min(locationLength + 35, len - startColumn));
if (startColumn + locationLength + 35 < len)
line += " ...";
- if (startColumn > 35) {
+ if (startColumn > 35)
+ {
line = " ... " + line;
startColumn = 40;
}
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/Scanner.cpp b/libsolidity/parsing/Scanner.cpp
index c9d5b969..0f6d6996 100644
--- a/libsolidity/parsing/Scanner.cpp
+++ b/libsolidity/parsing/Scanner.cpp
@@ -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();