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.cpp18
-rw-r--r--libsolidity/analysis/TypeChecker.cpp48
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp9
-rw-r--r--libsolidity/ast/Types.cpp74
-rw-r--r--libsolidity/ast/Types.h15
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp8
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp17
8 files changed, 154 insertions, 39 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..8a576e2e 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;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 143ac109..e023dc38 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -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),
@@ -1732,14 +1735,39 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
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 (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 = true;
+ if (auto const* contractType = dynamic_cast<ContractType const*>(argType.get()))
+ payable = contractType->isPayable();
+ resultType = make_shared<AddressType>(payable ? StateMutability::Payable : StateMutability::NonPayable);
+ }
}
_functionCall.annotation().type = resultType;
_functionCall.annotation().isPure = isPure;
@@ -2103,7 +2131,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(StateMutability::Payable).nativeMembers(nullptr))
if (addressMember.name == memberName)
{
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
@@ -2354,7 +2382,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 beab356c..8d52851a 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..25702f19 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
@@ -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
@@ -3275,7 +3327,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 +3336,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 +3344,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..a2d18b0a 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -321,15 +321,16 @@ class AddressType: public Type
public:
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 +338,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;
};
/**
@@ -755,7 +762,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 45e58bd0..bd863e05 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -1259,7 +1259,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, AddressType(type.isPayable() ? StateMutability::Payable : StateMutability::NonPayable), true);
m_context << identifier;
}
else
@@ -1277,15 +1277,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
utils().convertType(
*_memberAccess.expression().annotation().type,
- AddressType(),
+ AddressType(StateMutability::NonPayable),
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(StateMutability::NonPayable),
true
);
else