aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp8
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp10
-rw-r--r--libsolidity/analysis/StaticAnalyzer.h3
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp21
-rw-r--r--libsolidity/analysis/SyntaxChecker.h6
-rw-r--r--libsolidity/analysis/TypeChecker.cpp44
-rw-r--r--libsolidity/analysis/TypeChecker.h6
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp127
-rw-r--r--libsolidity/analysis/ViewPureChecker.h25
-rw-r--r--libsolidity/ast/AST.cpp4
-rw-r--r--libsolidity/ast/AST.h4
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp2
-rw-r--r--libsolidity/ast/Types.cpp150
-rw-r--r--libsolidity/ast/Types.h45
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp27
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp27
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp104
-rw-r--r--libsolidity/formal/SMTChecker.cpp5
-rw-r--r--libsolidity/formal/SSAVariable.cpp2
-rw-r--r--libsolidity/formal/SymbolicIntVariable.cpp14
-rw-r--r--libsolidity/parsing/Parser.cpp13
-rw-r--r--libsolidity/parsing/Token.cpp4
22 files changed, 402 insertions, 249 deletions
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index f33de7b7..8750b47b 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -332,7 +332,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
- case Location::Default: return "none";
+ case Location::Unspecified: return "none";
}
return {};
};
@@ -368,12 +368,12 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// Find correct data location.
if (_variable.isEventParameter())
{
- solAssert(varLoc == Location::Default, "");
+ solAssert(varLoc == Location::Unspecified, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
- solAssert(varLoc == Location::Default, "");
+ solAssert(varLoc == Location::Unspecified, "");
typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage;
}
else if (
@@ -394,7 +394,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
- case Location::Default:
+ case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 60a58665..487a5cca 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -56,7 +56,6 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
else
solAssert(!m_currentFunction, "");
solAssert(m_localVarUseCount.empty(), "");
- m_nonPayablePublic = _function.isPublic() && !_function.isPayable();
m_constructor = _function.isConstructor();
return true;
}
@@ -64,7 +63,6 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
void StaticAnalyzer::endVisit(FunctionDefinition const&)
{
m_currentFunction = nullptr;
- m_nonPayablePublic = false;
m_constructor = false;
for (auto const& var: m_localVarUseCount)
if (var.second == 0)
@@ -154,14 +152,6 @@ bool StaticAnalyzer::visit(MemberAccess const& _memberAccess)
);
}
- if (m_nonPayablePublic && !m_library)
- if (MagicType const* type = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type.get()))
- if (type->kind() == MagicType::Kind::Message && _memberAccess.memberName() == "value")
- m_errorReporter.warning(
- _memberAccess.location(),
- "\"msg.value\" used in non-payable function. Do you want to add the \"payable\" modifier to this function?"
- );
-
if (_memberAccess.memberName() == "callcode")
if (auto const* type = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
if (type->kind() == FunctionType::Kind::BareCallCode)
diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h
index 2a62e391..7f5c743a 100644
--- a/libsolidity/analysis/StaticAnalyzer.h
+++ b/libsolidity/analysis/StaticAnalyzer.h
@@ -75,9 +75,6 @@ private:
/// Flag that indicates whether the current contract definition is a library.
bool m_library = false;
- /// Flag that indicates whether a public function does not contain the "payable" modifier.
- bool m_nonPayablePublic = false;
-
/// Number of uses of each (named) local variable in a function, counter is initialized with zero.
/// Pairs of AST ids and pointers are used as keys to ensure a deterministic order
/// when traversing.
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index ac4fa72b..0bc20f2e 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -138,9 +138,25 @@ void SyntaxChecker::endVisit(ModifierDefinition const& _modifier)
m_placeholderFound = false;
}
-bool SyntaxChecker::visit(WhileStatement const&)
+void SyntaxChecker::checkSingleStatementVariableDeclaration(ASTNode const& _statement)
+{
+ auto varDecl = dynamic_cast<VariableDeclarationStatement const*>(&_statement);
+ if (varDecl)
+ m_errorReporter.syntaxError(_statement.location(), "Variable declarations can only be used inside blocks.");
+}
+
+bool SyntaxChecker::visit(IfStatement const& _ifStatement)
+{
+ checkSingleStatementVariableDeclaration(_ifStatement.trueStatement());
+ if (Statement const* _statement = _ifStatement.falseStatement())
+ checkSingleStatementVariableDeclaration(*_statement);
+ return true;
+}
+
+bool SyntaxChecker::visit(WhileStatement const& _whileStatement)
{
m_inLoopDepth++;
+ checkSingleStatementVariableDeclaration(_whileStatement.body());
return true;
}
@@ -149,9 +165,10 @@ void SyntaxChecker::endVisit(WhileStatement const&)
m_inLoopDepth--;
}
-bool SyntaxChecker::visit(ForStatement const&)
+bool SyntaxChecker::visit(ForStatement const& _forStatement)
{
m_inLoopDepth++;
+ checkSingleStatementVariableDeclaration(_forStatement.body());
return true;
}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 897df676..f5716bf9 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -52,6 +52,12 @@ private:
virtual bool visit(ModifierDefinition const& _modifier) override;
virtual void endVisit(ModifierDefinition const& _modifier) override;
+ /// Reports an error if _statement is a VariableDeclarationStatement.
+ /// Used by if/while/for to check for single statement variable declarations
+ /// without a block.
+ void checkSingleStatementVariableDeclaration(ASTNode const& _statement);
+
+ virtual bool visit(IfStatement const& _ifStatement) override;
virtual bool visit(WhileStatement const& _whileStatement) override;
virtual void endVisit(WhileStatement const& _whileStatement) override;
virtual bool visit(ForStatement const& _forStatement) override;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 3b1bfe5c..143ac109 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -525,7 +525,7 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
);
}
-TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
+TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2)
{
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
if (arguments.size() != 2)
@@ -544,10 +544,8 @@ TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall co
" to bytes memory requested."
);
- TypePointer returnType = make_shared<TupleType>();
-
if (arguments.size() < 2)
- return returnType;
+ return {};
// The following is a rather syntactic restriction, but we check it here anyway:
// The second argument has to be a tuple expression containing type names.
@@ -558,10 +556,10 @@ TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall co
arguments[1]->location(),
"The second argument to \"abi.decode\" has to be a tuple of types."
);
- return returnType;
+ return {};
}
- vector<TypePointer> components;
+ TypePointers components;
for (auto const& typeArgument: tupleExpression->components())
{
solAssert(typeArgument, "");
@@ -591,7 +589,7 @@ TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall co
components.push_back(make_shared<TupleType>());
}
}
- return make_shared<TupleType>(components);
+ return components;
}
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
@@ -686,9 +684,7 @@ bool TypeChecker::visit(StructDefinition const& _struct)
bool TypeChecker::visit(FunctionDefinition const& _function)
{
- bool isLibraryFunction =
- dynamic_cast<ContractDefinition const*>(_function.scope()) &&
- dynamic_cast<ContractDefinition const*>(_function.scope())->isLibrary();
+ bool isLibraryFunction = _function.inContractKind() == ContractDefinition::ContractKind::Library;
if (_function.isPayable())
{
if (isLibraryFunction)
@@ -1217,7 +1213,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (ref->dataStoredIn(DataLocation::Storage))
{
string errorText{"Uninitialized storage pointer."};
- if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
+ if (varDecl.referenceLocation() == VariableDeclaration::Location::Unspecified)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
solAssert(m_scope, "");
m_errorReporter.declarationError(varDecl.location(), errorText);
@@ -1782,15 +1778,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall())
m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version.");
- auto returnTypes =
- allowDynamicTypes ?
- functionType->returnParameterTypes() :
- functionType->returnParameterTypesWithoutDynamicTypes();
- if (returnTypes.size() == 1)
- _functionCall.annotation().type = returnTypes.front();
- else
- _functionCall.annotation().type = make_shared<TupleType>(returnTypes);
-
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::KECCAK256)
@@ -1826,8 +1813,14 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2);
+ // Will be assigned to .type at the end (turning multi-elements into a tuple).
+ TypePointers returnTypes =
+ allowDynamicTypes ?
+ functionType->returnParameterTypes() :
+ functionType->returnParameterTypesWithoutDynamicTypes();
+
if (functionType->kind() == FunctionType::Kind::ABIDecode)
- _functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
+ returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size())
{
solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, "");
@@ -1985,6 +1978,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
}
}
+ if (returnTypes.size() == 1)
+ _functionCall.annotation().type = returnTypes.front();
+ else
+ _functionCall.annotation().type = make_shared<TupleType>(returnTypes);
+
return false;
}
@@ -2105,7 +2103,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: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr))
+ for (auto const& addressMember: AddressType().nativeMembers(nullptr))
if (addressMember.name == memberName)
{
Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
@@ -2356,7 +2354,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<IntegerType>(160, IntegerType::Modifier::Address);
+ _literal.annotation().type = make_shared<AddressType>();
string msg;
if (_literal.value().length() != 42) // "0x" + 40 hex digits
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 4be0d1e4..8d25a88e 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -92,9 +92,9 @@ private:
void checkExpressionAssignment(Type const& _type, Expression const& _expression);
/// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the
- /// return type (which is basically the second argument) if successful. It returns
- /// the empty tuple type or error.
- TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
+ /// vector of return types (which is basically the second argument) if successful. It returns
+ /// the empty vector on error.
+ TypePointers typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2);
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
virtual void endVisit(UsingForDirective const& _usingFor) override;
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index e92ad2fa..113a3177 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -142,7 +142,7 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef)
{
solAssert(!m_currentFunction, "");
m_currentFunction = &_funDef;
- m_currentBestMutability = StateMutability::Pure;
+ m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()};
return true;
}
@@ -150,7 +150,7 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
{
solAssert(m_currentFunction == &_funDef, "");
if (
- m_currentBestMutability < _funDef.stateMutability() &&
+ m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() &&
_funDef.stateMutability() != StateMutability::Payable &&
_funDef.isImplemented() &&
!_funDef.isConstructor() &&
@@ -159,22 +159,22 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef)
)
m_errorReporter.warning(
_funDef.location(),
- "Function state mutability can be restricted to " + stateMutabilityToString(m_currentBestMutability)
+ "Function state mutability can be restricted to " + stateMutabilityToString(m_bestMutabilityAndLocation.mutability)
);
m_currentFunction = nullptr;
}
-bool ViewPureChecker::visit(ModifierDefinition const&)
+bool ViewPureChecker::visit(ModifierDefinition const& _modifier)
{
solAssert(m_currentFunction == nullptr, "");
- m_currentBestMutability = StateMutability::Pure;
+ m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()};
return true;
}
void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef)
{
solAssert(m_currentFunction == nullptr, "");
- m_inferredMutability[&_modifierDef] = m_currentBestMutability;
+ m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation);
}
void ViewPureChecker::endVisit(Identifier const& _identifier)
@@ -219,36 +219,72 @@ void ViewPureChecker::endVisit(InlineAssembly const& _inlineAssembly)
}(_inlineAssembly.operations());
}
-void ViewPureChecker::reportMutability(StateMutability _mutability, SourceLocation const& _location)
+void ViewPureChecker::reportMutability(
+ StateMutability _mutability,
+ SourceLocation const& _location,
+ boost::optional<SourceLocation> const& _nestedLocation
+)
{
- if (m_currentFunction && m_currentFunction->stateMutability() < _mutability)
+ if (_mutability > m_bestMutabilityAndLocation.mutability)
+ m_bestMutabilityAndLocation = MutabilityAndLocation{_mutability, _location};
+ if (!m_currentFunction || _mutability <= m_currentFunction->stateMutability())
+ return;
+
+ // Check for payable here, because any occurrence of `msg.value`
+ // will set mutability to payable.
+ if (_mutability == StateMutability::View || (
+ _mutability == StateMutability::Payable &&
+ m_currentFunction->stateMutability() == StateMutability::Pure
+ ))
{
- if (_mutability == StateMutability::View)
- m_errorReporter.typeError(
- _location,
- "Function declared as pure, but this expression (potentially) reads from the "
- "environment or state and thus requires \"view\"."
- );
- else if (_mutability == StateMutability::NonPayable)
- m_errorReporter.typeError(
- _location,
- "Function declared as " +
- stateMutabilityToString(m_currentFunction->stateMutability()) +
- ", but this expression (potentially) modifies the state and thus "
- "requires non-payable (the default) or payable."
- );
- else
- solAssert(false, "");
-
- solAssert(
- m_currentFunction->stateMutability() == StateMutability::View ||
- m_currentFunction->stateMutability() == StateMutability::Pure,
- ""
+ m_errorReporter.typeError(
+ _location,
+ "Function declared as pure, but this expression (potentially) reads from the "
+ "environment or state and thus requires \"view\"."
);
m_errors = true;
}
- if (_mutability > m_currentBestMutability)
- m_currentBestMutability = _mutability;
+ else if (_mutability == StateMutability::NonPayable)
+ {
+ m_errorReporter.typeError(
+ _location,
+ "Function declared as " +
+ stateMutabilityToString(m_currentFunction->stateMutability()) +
+ ", but this expression (potentially) modifies the state and thus "
+ "requires non-payable (the default) or payable."
+ );
+ m_errors = true;
+ }
+ else if (_mutability == StateMutability::Payable)
+ {
+ // We do not warn for library functions because they cannot be payable anyway.
+ // Also internal functions should be allowed to use `msg.value`.
+ if (m_currentFunction->isPublic() && m_currentFunction->inContractKind() != ContractDefinition::ContractKind::Library)
+ {
+ if (_nestedLocation)
+ m_errorReporter.typeError(
+ _location,
+ SecondarySourceLocation().append("\"msg.value\" appears here inside the modifier.", *_nestedLocation),
+ "This modifier uses \"msg.value\" and thus the function has to be payable or internal."
+ );
+ else
+ m_errorReporter.typeError(
+ _location,
+ "\"msg.value\" can only be used in payable public functions. Make the function "
+ "\"payable\" or use an internal function to avoid this error."
+ );
+ m_errors = true;
+ }
+ }
+ else
+ solAssert(false, "");
+
+ solAssert(
+ m_currentFunction->stateMutability() == StateMutability::View ||
+ m_currentFunction->stateMutability() == StateMutability::Pure ||
+ m_currentFunction->stateMutability() == StateMutability::NonPayable,
+ ""
+ );
}
void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
@@ -287,18 +323,34 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
ASTString const& member = _memberAccess.memberName();
switch (_memberAccess.expression().annotation().type->category())
{
- case Type::Category::Integer:
+ case Type::Category::Address:
if (member == "balance")
mutability = StateMutability::View;
break;
case Type::Category::Magic:
{
- // we can ignore the kind of magic and only look at the name of the member
- set<string> static const pureMembers{
- "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash"
+ using MagicMember = pair<MagicType::Kind, string>;
+ set<MagicMember> static const pureMembers{
+ {MagicType::Kind::ABI, "decode"},
+ {MagicType::Kind::ABI, "encode"},
+ {MagicType::Kind::ABI, "encodePacked"},
+ {MagicType::Kind::ABI, "encodeWithSelector"},
+ {MagicType::Kind::ABI, "encodeWithSignature"},
+ {MagicType::Kind::Block, "blockhash"},
+ {MagicType::Kind::Message, "data"},
+ {MagicType::Kind::Message, "sig"}
};
- if (!pureMembers.count(member))
+ set<MagicMember> static const payableMembers{
+ {MagicType::Kind::Message, "value"}
+ };
+
+ auto const& type = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type);
+ MagicMember magicMember(type.kind(), member);
+
+ if (!pureMembers.count(magicMember))
mutability = StateMutability::View;
+ if (payableMembers.count(magicMember))
+ mutability = StateMutability::Payable;
break;
}
case Type::Category::Struct:
@@ -338,7 +390,8 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier)
if (ModifierDefinition const* mod = dynamic_cast<decltype(mod)>(_modifier.name()->annotation().referencedDeclaration))
{
solAssert(m_inferredMutability.count(mod), "");
- reportMutability(m_inferredMutability.at(mod), _modifier.location());
+ auto const& mutAndLocation = m_inferredMutability.at(mod);
+ reportMutability(mutAndLocation.mutability, _modifier.location(), mutAndLocation.location);
}
else
solAssert(dynamic_cast<ContractDefinition const*>(_modifier.name()->annotation().referencedDeclaration), "");
diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h
index 3db52e7e..faa5b698 100644
--- a/libsolidity/analysis/ViewPureChecker.h
+++ b/libsolidity/analysis/ViewPureChecker.h
@@ -31,16 +31,6 @@ namespace dev
namespace solidity
{
-class ASTNode;
-class FunctionDefinition;
-class ModifierDefinition;
-class Identifier;
-class MemberAccess;
-class IndexAccess;
-class ModifierInvocation;
-class FunctionCall;
-class InlineAssembly;
-
class ViewPureChecker: private ASTConstVisitor
{
public:
@@ -50,6 +40,11 @@ public:
bool check();
private:
+ struct MutabilityAndLocation
+ {
+ StateMutability mutability;
+ SourceLocation location;
+ };
virtual bool visit(FunctionDefinition const& _funDef) override;
virtual void endVisit(FunctionDefinition const& _funDef) override;
@@ -65,15 +60,19 @@ private:
/// Called when an element of mutability @a _mutability is encountered.
/// Creates appropriate warnings and errors and sets @a m_currentBestMutability.
- void reportMutability(StateMutability _mutability, SourceLocation const& _location);
+ void reportMutability(
+ StateMutability _mutability,
+ SourceLocation const& _location,
+ boost::optional<SourceLocation> const& _nestedLocation = {}
+ );
std::vector<std::shared_ptr<ASTNode>> const& m_ast;
ErrorReporter& m_errorReporter;
bool m_errors = false;
- StateMutability m_currentBestMutability = StateMutability::Payable;
+ MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, SourceLocation()};
FunctionDefinition const* m_currentFunction = nullptr;
- std::map<ModifierDefinition const*, StateMutability> m_inferredMutability;
+ std::map<ModifierDefinition const*, MutabilityAndLocation> m_inferredMutability;
};
}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 635ab024..8e7a81a6 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -517,7 +517,7 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
using Location = VariableDeclaration::Location;
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
- return set<Location>{ Location::Default };
+ return set<Location>{ Location::Unspecified };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
@@ -546,7 +546,7 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
}
else
// Struct members etc.
- return set<Location>{ Location::Default };
+ return set<Location>{ Location::Unspecified };
}
TypePointer VariableDeclaration::type() const
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index b953211d..a5cd277d 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -655,7 +655,7 @@ private:
class VariableDeclaration: public Declaration
{
public:
- enum Location { Default, Storage, Memory, CallData };
+ enum Location { Unspecified, Storage, Memory, CallData };
VariableDeclaration(
SourceLocation const& _sourceLocation,
@@ -666,7 +666,7 @@ public:
bool _isStateVar = false,
bool _isIndexed = false,
bool _isConstant = false,
- Location _referenceLocation = Location::Default
+ Location _referenceLocation = Location::Unspecified
):
Declaration(_sourceLocation, _name, _visibility),
m_typeName(_type),
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 72b20b3b..beab356c 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -739,7 +739,7 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
{
switch (_location)
{
- case VariableDeclaration::Location::Default:
+ case VariableDeclaration::Location::Unspecified:
return "default";
case VariableDeclaration::Location::Storage:
return "storage";
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index b1cd15b4..a302203b 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<IntegerType>(160, IntegerType::Modifier::Address);
+ return make_shared<AddressType>();
case Token::Bool:
return make_shared<BoolType>();
case Token::Bytes:
@@ -439,6 +439,59 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
return members;
}
+string AddressType::richIdentifier() const
+{
+ return "t_address";
+}
+
+bool AddressType::isExplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ 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
+{
+ return "address";
+}
+
+u256 AddressType::literalValue(Literal const* _literal) const
+{
+ solAssert(_literal, "");
+ solAssert(_literal->value().substr(0, 2) == "0x", "");
+ return u256(_literal->value());
+}
+
+TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const
+{
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
+}
+
+
+TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+{
+ // Addresses can only be compared.
+ if (!Token::isCompareOp(_operator))
+ return TypePointer();
+
+ return Type::commonType(shared_from_this(), _other);
+}
+
+MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const
+{
+ return {
+ {"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)}
+ };
+}
+
namespace
{
@@ -448,7 +501,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
if (_operator == Token::SHR)
return false;
else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType))
- return !otherInt->isAddress();
+ return true;
else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType))
return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned();
else
@@ -460,8 +513,6 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT
IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier):
m_bits(_bits), m_modifier(_modifier)
{
- if (isAddress())
- solAssert(m_bits == 160, "");
solAssert(
m_bits > 0 && m_bits <= 256 && m_bits % 8 == 0,
"Invalid bit number for integer type: " + dev::toString(m_bits)
@@ -470,10 +521,7 @@ IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier):
string IntegerType::richIdentifier() const
{
- if (isAddress())
- return "t_address";
- else
- return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
+ return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
}
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@@ -483,8 +531,6 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (convertTo.m_bits < m_bits)
return false;
- if (isAddress())
- return convertTo.isAddress();
else if (isSigned())
return convertTo.isSigned();
else
@@ -493,11 +539,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
else if (_convertTo.category() == Category::FixedPoint)
{
FixedPointType const& convertTo = dynamic_cast<FixedPointType const&>(_convertTo);
-
- if (isAddress())
- return false;
- else
- return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue();
+ return maxValue() <= convertTo.maxIntegerValue() && minValue() >= convertTo.minIntegerValue();
}
else
return false;
@@ -506,6 +548,7 @@ bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return _convertTo.category() == category() ||
+ _convertTo.category() == Category::Address ||
_convertTo.category() == Category::Contract ||
_convertTo.category() == Category::Enum ||
(_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8) ||
@@ -517,10 +560,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
// "delete" is ok for all integer types
if (_operator == Token::Delete)
return make_shared<TupleType>();
- // no further unary operators for addresses
- else if (isAddress())
- return TypePointer();
- // for non-address integers, we allow +, -, ++ and --
+ // we allow +, -, ++ and --
else if (_operator == Token::Add || _operator == Token::Sub ||
_operator == Token::Inc || _operator == Token::Dec ||
_operator == Token::BitNot)
@@ -539,20 +579,10 @@ bool IntegerType::operator==(Type const& _other) const
string IntegerType::toString(bool) const
{
- if (isAddress())
- return "address";
string prefix = isSigned() ? "int" : "uint";
return prefix + dev::toString(m_bits);
}
-u256 IntegerType::literalValue(Literal const* _literal) const
-{
- solAssert(m_modifier == Modifier::Address, "");
- solAssert(_literal, "");
- solAssert(_literal->value().substr(0, 2) == "0x", "");
- return u256(_literal->value());
-}
-
bigint IntegerType::minValue() const
{
if (isSigned())
@@ -580,8 +610,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
if (Token::isShiftOp(_operator))
{
// Shifts are not symmetric with respect to the type
- if (isAddress())
- return TypePointer();
if (isValidShiftAndAmountType(_operator, *_other))
return shared_from_this();
else
@@ -599,9 +627,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return TypePointer();
if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType))
{
- // Nothing else can be done with addresses
- if (intType->isAddress())
- return TypePointer();
// Signed EXP is not allowed
if (Token::Exp == _operator && intType->isSigned())
return TypePointer();
@@ -612,22 +637,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
return commonType;
}
-MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const
-{
- if (isAddress())
- return {
- {"balance", make_shared<IntegerType>(256)},
- {"call", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCall, false, StateMutability::Payable)},
- {"callcode", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareCallCode, false, StateMutability::Payable)},
- {"delegatecall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareDelegateCall, false)},
- {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
- {"staticcall", make_shared<FunctionType>(strings{"bytes memory"}, strings{"bool"}, FunctionType::Kind::BareStaticCall, false, StateMutability::View)},
- {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
- };
- else
- return MemberList::MemberMap();
-}
-
FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits, FixedPointType::Modifier _modifier):
m_totalBits(_totalBits), m_fractionalDigits(_fractionalDigits), m_modifier(_modifier)
{
@@ -658,8 +667,7 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return _convertTo.category() == category() ||
- (_convertTo.category() == Category::Integer && !dynamic_cast<IntegerType const&>(_convertTo).isAddress());
+ return _convertTo.category() == category() || _convertTo.category() == Category::Integer;
}
TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const
@@ -912,8 +920,6 @@ bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
if (isFractional())
return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
- if (targetType.isAddress())
- return false;
if (m_value == rational(0))
return true;
unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
@@ -1368,6 +1374,7 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
return (_convertTo.category() == Category::Integer && numBytes() * 8 == dynamic_cast<IntegerType const&>(_convertTo).numBits()) ||
+ (_convertTo.category() == Category::Address && numBytes() == 20) ||
_convertTo.category() == Category::FixedPoint ||
_convertTo.category() == category();
}
@@ -1469,9 +1476,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return
- isImplicitlyConvertibleTo(_convertTo) ||
- _convertTo == IntegerType(160, IntegerType::Modifier::Address);
+ return isImplicitlyConvertibleTo(_convertTo) || _convertTo.category() == Category::Address;
}
bool ContractType::isPayable() const
@@ -1501,8 +1506,6 @@ TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
return make_shared<TupleType>();
case DataLocation::Storage:
return m_isPointer ? TypePointer() : make_shared<TupleType>();
- default:
- solAssert(false, "");
}
return TypePointer();
}
@@ -1548,8 +1551,6 @@ string ReferenceType::identifierLocationSuffix() const
case DataLocation::CallData:
id += "_calldata";
break;
- default:
- solAssert(false, "Unknown location returned by location()");
}
if (isPointer())
id += "_ptr";
@@ -2496,7 +2497,14 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const
{
TypePointers returnParameterTypes = m_returnParameterTypes;
- if (m_kind == Kind::External || m_kind == Kind::CallCode || m_kind == Kind::DelegateCall)
+ if (
+ m_kind == Kind::External ||
+ m_kind == Kind::DelegateCall ||
+ m_kind == Kind::BareCall ||
+ m_kind == Kind::BareCallCode ||
+ m_kind == Kind::BareDelegateCall ||
+ m_kind == Kind::BareStaticCall
+ )
for (auto& param: returnParameterTypes)
if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage))
param = make_shared<InaccessibleDynamicType>();
@@ -2518,7 +2526,6 @@ string FunctionType::richIdentifier() const
{
case Kind::Internal: id += "internal"; break;
case Kind::External: id += "external"; break;
- case Kind::CallCode: id += "callcode"; break;
case Kind::DelegateCall: id += "delegatecall"; break;
case Kind::BareCall: id += "barecall"; break;
case Kind::BareCallCode: id += "barecallcode"; break;
@@ -2556,7 +2563,6 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
case Kind::ABIDecode: id += "abidecode"; break;
- default: solAssert(false, "Unknown function location."); break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@@ -2583,12 +2589,8 @@ bool FunctionType::operator==(Type const& _other) const
bool FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- if (m_kind == Kind::External && _convertTo.category() == Category::Integer)
- {
- IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
- if (convertTo.isAddress())
+ if (m_kind == Kind::External && _convertTo.category() == Category::Address)
return true;
- }
return _convertTo.category() == category();
}
@@ -2700,7 +2702,6 @@ unsigned FunctionType::sizeOnStack() const
switch(kind)
{
case Kind::External:
- case Kind::CallCode:
case Kind::DelegateCall:
size = 2;
break;
@@ -2933,7 +2934,6 @@ string FunctionType::externalSignature() const
{
case Kind::Internal:
case Kind::External:
- case Kind::CallCode:
case Kind::DelegateCall:
case Kind::Event:
break;
@@ -3257,8 +3257,6 @@ string MagicType::richIdentifier() const
return "t_magic_transaction";
case Kind::ABI:
return "t_magic_abi";
- default:
- solAssert(false, "Unknown kind of magic");
}
return "";
}
@@ -3277,7 +3275,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
{
case Kind::Block:
return MemberList::MemberMap({
- {"coinbase", make_shared<IntegerType>(160, IntegerType::Modifier::Address)},
+ {"coinbase", make_shared<AddressType>()},
{"timestamp", make_shared<IntegerType>(256)},
{"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Kind::BlockHash, false, StateMutability::View)},
{"difficulty", make_shared<IntegerType>(256)},
@@ -3286,7 +3284,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
});
case Kind::Message:
return MemberList::MemberMap({
- {"sender", make_shared<IntegerType>(160, IntegerType::Modifier::Address)},
+ {"sender", make_shared<AddressType>()},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)},
@@ -3294,7 +3292,7 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
});
case Kind::Transaction:
return MemberList::MemberMap({
- {"origin", make_shared<IntegerType>(160, IntegerType::Modifier::Address)},
+ {"origin", make_shared<AddressType>()},
{"gasprice", make_shared<IntegerType>(256)}
});
case Kind::ABI:
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 0b1b5d6d..7ee66838 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -141,7 +141,7 @@ public:
virtual ~Type() = default;
enum class Category
{
- Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
+ Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array,
FixedBytes, Contract, Struct, Function, Enum, Tuple,
Mapping, TypeType, Modifier, Magic, Module,
InaccessibleDynamic
@@ -314,14 +314,45 @@ protected:
};
/**
- * Any kind of integer type (signed, unsigned, address).
+ * Type for addresses.
+ */
+class AddressType: public Type
+{
+public:
+ virtual Category category() const override { return Category::Address; }
+
+ explicit AddressType()
+ {
+ }
+
+ virtual std::string richIdentifier() 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 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; }
+
+ virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
+
+ virtual std::string toString(bool _short) 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(); }
+};
+
+/**
+ * Any kind of integer type (signed, unsigned).
*/
class IntegerType: public Type
{
public:
enum class Modifier
{
- Unsigned, Signed, Address
+ Unsigned, Signed
};
virtual Category category() const override { return Category::Integer; }
@@ -339,17 +370,12 @@ public:
virtual unsigned storageBytes() const override { return m_bits / 8; }
virtual bool isValueType() const override { return true; }
- virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
-
virtual std::string toString(bool _short) 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(); }
unsigned numBits() const { return m_bits; }
- bool isAddress() const { return m_modifier == Modifier::Address; }
bool isSigned() const { return m_modifier == Modifier::Signed; }
bigint minValue() const;
@@ -729,7 +755,7 @@ public:
{
if (isSuper())
return TypePointer{};
- return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
+ return std::make_shared<AddressType>();
}
virtual TypePointer interfaceType(bool _inLibrary) const override
{
@@ -899,7 +925,6 @@ public:
{
Internal, ///< stack-call using plain JUMP
External, ///< external call using CALL
- CallCode, ///< external call using CALLCODE, i.e. not exchanging the storage
DelegateCall, ///< external call using DELEGATECALL, i.e. not exchanging the storage
BareCall, ///< CALL without function hash
BareCallCode, ///< CALLCODE without function hash
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index dda77958..5e5fe84a 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -197,6 +197,9 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
templ("functionName", functionName);
switch (_type.category())
{
+ case Type::Category::Address:
+ templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
+ break;
case Type::Category::Integer:
{
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
@@ -239,7 +242,7 @@ string ABIFunctions::cleanupFunction(Type const& _type, bool _revertOnFailure)
break;
}
case Type::Category::Contract:
- templ("body", "cleaned := " + cleanupFunction(IntegerType(160, IntegerType::Modifier::Address)) + "(value)");
+ templ("body", "cleaned := " + cleanupFunction(AddressType()) + "(value)");
break;
case Type::Category::Enum:
{
@@ -284,6 +287,12 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
auto fromCategory = _from.category();
switch (fromCategory)
{
+ case Type::Category::Address:
+ body =
+ Whiskers("converted := <convert>(value)")
+ ("convert", conversionFunction(IntegerType(160), _to))
+ .render();
+ break;
case Type::Category::Integer:
case Type::Category::RationalNumber:
case Type::Category::Contract:
@@ -314,16 +323,19 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
.render();
}
else if (toCategory == Type::Category::FixedPoint)
- {
solUnimplemented("Not yet implemented - FixedPointType.");
- }
+ else if (toCategory == Type::Category::Address)
+ body =
+ Whiskers("converted := <convert>(value)")
+ ("convert", conversionFunction(_from, IntegerType(160)))
+ .render();
else
{
solAssert(
toCategory == Type::Category::Integer ||
toCategory == Type::Category::Contract,
"");
- IntegerType const addressType(160, IntegerType::Modifier::Address);
+ IntegerType const addressType(160);
IntegerType const& to =
toCategory == Type::Category::Integer ?
dynamic_cast<IntegerType const&>(_to) :
@@ -375,6 +387,11 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
("shift", shiftRightFunction(256 - from.numBytes() * 8))
("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
.render();
+ else if (toCategory == Type::Category::Address)
+ body =
+ Whiskers("converted := <convert>(value)")
+ ("convert", conversionFunction(_from, IntegerType(160)))
+ .render();
else
{
// clear for conversion to longer bytes
@@ -410,7 +427,7 @@ string ABIFunctions::conversionFunction(Type const& _from, Type const& _to)
solAssert(false, "");
}
- solAssert(!body.empty(), "");
+ solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
templ("body", body);
return templ.render();
});
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index b30851fb..e6ad6d9c 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -657,6 +657,11 @@ void CompilerUtils::convertType(
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
+ else if (targetTypeCategory == Type::Category::Address)
+ {
+ solAssert(typeOnStack.numBytes() * 8 == 160, "");
+ rightShiftNumberOnStack(256 - 160);
+ }
else
{
// clear for conversion to longer bytes
@@ -690,23 +695,33 @@ void CompilerUtils::convertType(
break;
case Type::Category::FixedPoint:
solUnimplemented("Not yet implemented - FixedPointType.");
+ case Type::Category::Address:
case Type::Category::Integer:
case Type::Category::Contract:
case Type::Category::RationalNumber:
if (targetTypeCategory == Type::Category::FixedBytes)
{
- solAssert(stackTypeCategory == Type::Category::Integer || stackTypeCategory == Type::Category::RationalNumber,
- "Invalid conversion to FixedBytesType requested.");
+ solAssert(
+ stackTypeCategory == Type::Category::Address ||
+ stackTypeCategory == Type::Category::Integer ||
+ stackTypeCategory == Type::Category::RationalNumber,
+ "Invalid conversion to FixedBytesType requested."
+ );
// conversion from bytes to string. no need to clean the high bit
// only to shift left because of opposite alignment
FixedBytesType const& targetBytesType = dynamic_cast<FixedBytesType const&>(_targetType);
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
+ {
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
+ }
+ else if (stackTypeCategory == Type::Category::Address)
+ solAssert(targetBytesType.numBytes() * 8 == 160, "");
leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
}
else if (targetTypeCategory == Type::Category::Enum)
{
+ solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested.");
solAssert(_typeOnStack.mobileType(), "");
// just clean
convertType(_typeOnStack, *_typeOnStack.mobileType(), true);
@@ -733,8 +748,8 @@ void CompilerUtils::convertType(
}
else
{
- solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract, "");
- IntegerType addressType(160, IntegerType::Modifier::Address);
+ solAssert(targetTypeCategory == Type::Category::Integer || targetTypeCategory == Type::Category::Contract || targetTypeCategory == Type::Category::Address, "");
+ IntegerType addressType(160);
IntegerType const& targetType = targetTypeCategory == Type::Category::Integer
? dynamic_cast<IntegerType const&>(_targetType) : addressType;
if (stackTypeCategory == Type::Category::RationalNumber)
@@ -996,10 +1011,8 @@ void CompilerUtils::convertType(
m_context << Instruction::ISZERO << Instruction::ISZERO;
break;
default:
- if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Integer)
+ if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address)
{
- IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
- solAssert(targetType.isAddress(), "Function type can only be converted to address.");
FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted.");
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 53a06090..4150bc11 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -281,19 +281,19 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
if (_tuple.isInlineArray())
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type);
-
+
solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array.");
m_context << max(u256(32u), arrayType.memorySize());
utils().allocateMemory();
m_context << Instruction::DUP1;
-
+
for (auto const& component: _tuple.components())
{
component->accept(*this);
utils().convertType(*component->annotation().type, *arrayType.baseType(), true);
- utils().storeInMemoryDynamic(*arrayType.baseType(), true);
+ utils().storeInMemoryDynamic(*arrayType.baseType(), true);
}
-
+
m_context << Instruction::POP;
}
else
@@ -566,7 +566,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
break;
}
case FunctionType::Kind::External:
- case FunctionType::Kind::CallCode:
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
@@ -1075,7 +1074,11 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
{
arguments.front()->accept(*this);
TypePointer firstArgType = arguments.front()->annotation().type;
- TypePointers const& targetTypes = dynamic_cast<TupleType const&>(*_functionCall.annotation().type).components();
+ TypePointers targetTypes;
+ if (TupleType const* targetTupleType = dynamic_cast<TupleType const*>(_functionCall.annotation().type.get()))
+ targetTypes = targetTupleType->components();
+ else
+ targetTypes = TypePointers{_functionCall.annotation().type};
if (
*firstArgType == ArrayType(DataLocation::CallData) ||
*firstArgType == ArrayType(DataLocation::CallData, true)
@@ -1169,7 +1172,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
_memberAccess.expression().accept(*this);
m_context << funType->externalIdentifier();
break;
- case FunctionType::Kind::CallCode:
case FunctionType::Kind::External:
case FunctionType::Kind::Creation:
case FunctionType::Kind::Send:
@@ -1257,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, IntegerType(160, IntegerType::Modifier::Address), true);
+ utils().convertType(type, AddressType(), true);
m_context << identifier;
}
else
@@ -1266,11 +1268,16 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
}
case Type::Category::Integer:
{
+ solAssert(false, "Invalid member access to integer");
+ break;
+ }
+ case Type::Category::Address:
+ {
if (member == "balance")
{
utils().convertType(
*_memberAccess.expression().annotation().type,
- IntegerType(160, IntegerType::Modifier::Address),
+ AddressType(),
true
);
m_context << Instruction::BALANCE;
@@ -1278,11 +1285,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall", "staticcall"}).count(member))
utils().convertType(
*_memberAccess.expression().annotation().type,
- IntegerType(160, IntegerType::Modifier::Address),
+ AddressType(),
true
);
else
- solAssert(false, "Invalid member access to integer");
+ solAssert(false, "Invalid member access to address");
break;
}
case Type::Category::Function:
@@ -1571,12 +1578,12 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
{
CompilerContext::LocationSetter locationSetter(m_context, _literal);
TypePointer type = _literal.annotation().type;
-
+
switch (type->category())
{
case Type::Category::RationalNumber:
case Type::Category::Bool:
- case Type::Category::Integer:
+ case Type::Category::Address:
m_context << type->literalValue(&_literal);
break;
case Type::Category::StringLiteral:
@@ -1829,33 +1836,34 @@ void ExpressionCompiler::appendExternalFunctionCall(
auto funKind = _functionType.kind();
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
-
- bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
- bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
+
+ bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
+ bool isCallCode = funKind == FunctionType::Kind::BareCallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
unsigned retSize = 0;
- TypePointers returnTypes;
- if (returnSuccessCondition)
- retSize = 0; // return value actually is success condition
- else if (haveReturndatacopy)
- returnTypes = _functionType.returnParameterTypes();
- else
- returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes();
-
bool dynamicReturnSize = false;
- for (auto const& retType: returnTypes)
- if (retType->isDynamicallyEncoded())
- {
- solAssert(haveReturndatacopy, "");
- dynamicReturnSize = true;
- retSize = 0;
- break;
- }
+ TypePointers returnTypes;
+ if (!returnSuccessConditionAndReturndata)
+ {
+ if (haveReturndatacopy)
+ returnTypes = _functionType.returnParameterTypes();
else
- retSize += retType->calldataEncodedSize();
+ returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes();
+
+ for (auto const& retType: returnTypes)
+ if (retType->isDynamicallyEncoded())
+ {
+ solAssert(haveReturndatacopy, "");
+ dynamicReturnSize = true;
+ retSize = 0;
+ break;
+ }
+ else
+ retSize += retType->calldataEncodedSize();
+ }
// Evaluate arguments.
TypePointers argumentTypes;
@@ -1959,7 +1967,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
bool existenceChecked = false;
// Check the the target contract exists (has code) for non-low-level calls.
- if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::CallCode || funKind == FunctionType::Kind::DelegateCall)
+ if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall)
{
m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO;
// TODO: error message?
@@ -1999,7 +2007,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
(_functionType.gasSet() ? 1 : 0) +
(!_functionType.isBareCall() ? 1 : 0);
- if (returnSuccessCondition)
+ if (returnSuccessConditionAndReturndata)
m_context << swapInstruction(remainsSize);
else
{
@@ -2010,9 +2018,31 @@ void ExpressionCompiler::appendExternalFunctionCall(
utils().popStackSlots(remainsSize);
- if (returnSuccessCondition)
+ if (returnSuccessConditionAndReturndata)
{
- // already there
+ // success condition is already there
+ // The return parameter types can be empty, when this function is used as
+ // an internal helper function e.g. for ``send`` and ``transfer``. In that
+ // case we're only interested in the success condition, not the return data.
+ if (!_functionType.returnParameterTypes().empty())
+ {
+ if (haveReturndatacopy)
+ {
+ m_context << Instruction::RETURNDATASIZE;
+ m_context.appendInlineAssembly(R"({
+ switch v case 0 {
+ v := 0x60
+ } default {
+ v := mload(0x40)
+ mstore(0x40, add(v, and(add(returndatasize(), 0x3f), not(0x1f))))
+ mstore(v, returndatasize())
+ returndatacopy(add(v, 0x20), 0, returndatasize())
+ }
+ })", {"v"});
+ }
+ else
+ utils().pushZeroPointer();
+ }
}
else if (funKind == FunctionType::Kind::RIPEMD160)
{
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 88c1e56a..49c90405 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -394,7 +394,7 @@ void SMTChecker::endVisit(Identifier const& _identifier)
void SMTChecker::endVisit(Literal const& _literal)
{
Type const& type = *_literal.annotation().type;
- if (type.category() == Type::Category::Integer || type.category() == Type::Category::RationalNumber)
+ if (type.category() == Type::Category::Integer || type.category() == Type::Category::Address || type.category() == Type::Category::RationalNumber)
{
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&type))
solAssert(!rational->isFractional(), "");
@@ -540,6 +540,8 @@ void SMTChecker::assignment(VariableDeclaration const& _variable, smt::Expressio
TypePointer type = _variable.type();
if (auto const* intType = dynamic_cast<IntegerType const*>(type.get()))
checkUnderOverflow(_value, *intType, _location);
+ else if (dynamic_cast<AddressType const*>(type.get()))
+ checkUnderOverflow(_value, IntegerType(160), _location);
m_interface->addAssertion(newValue(_variable) == _value);
}
@@ -862,6 +864,7 @@ void SMTChecker::createExpr(Expression const& _e)
m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
break;
}
+ case Type::Category::Address:
case Type::Category::Integer:
m_expressions.emplace(&_e, m_interface->newInteger(uniqueSymbol(_e)));
break;
diff --git a/libsolidity/formal/SSAVariable.cpp b/libsolidity/formal/SSAVariable.cpp
index f3213e03..4fc2dd45 100644
--- a/libsolidity/formal/SSAVariable.cpp
+++ b/libsolidity/formal/SSAVariable.cpp
@@ -50,7 +50,7 @@ bool SSAVariable::isSupportedType(Type::Category _category)
bool SSAVariable::isInteger(Type::Category _category)
{
- return _category == Type::Category::Integer;
+ return _category == Type::Category::Integer || _category == Type::Category::Address;
}
bool SSAVariable::isBool(Type::Category _category)
diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp
index 5e71fdcc..4f65b1fd 100644
--- a/libsolidity/formal/SymbolicIntVariable.cpp
+++ b/libsolidity/formal/SymbolicIntVariable.cpp
@@ -29,7 +29,11 @@ SymbolicIntVariable::SymbolicIntVariable(
):
SymbolicVariable(_decl, _interface)
{
- solAssert(m_declaration.type()->category() == Type::Category::Integer, "");
+ solAssert(
+ m_declaration.type()->category() == Type::Category::Integer ||
+ m_declaration.type()->category() == Type::Category::Address,
+ ""
+ );
}
smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const
@@ -44,9 +48,11 @@ void SymbolicIntVariable::setZeroValue(int _seq)
void SymbolicIntVariable::setUnknownValue(int _seq)
{
- auto const& intType = dynamic_cast<IntegerType const&>(*m_declaration.type());
- m_interface.addAssertion(valueAtSequence(_seq) >= minValue(intType));
- m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(intType));
+ auto intType = dynamic_pointer_cast<IntegerType const>(m_declaration.type());
+ if (!intType)
+ intType = make_shared<IntegerType>(160);
+ m_interface.addAssertion(valueAtSequence(_seq) >= minValue(*intType));
+ m_interface.addAssertion(valueAtSequence(_seq) <= maxValue(*intType));
}
smt::Expression SymbolicIntVariable::minValue(IntegerType const& _t)
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 0bee2a91..c9c26f57 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -564,7 +564,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
bool isIndexed = false;
bool isDeclaredConst = false;
Declaration::Visibility visibility(Declaration::Visibility::Default);
- VariableDeclaration::Location location = VariableDeclaration::Location::Default;
+ VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified;
ASTPointer<ASTString> identifier;
while (true)
@@ -572,6 +572,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
Token::Value token = m_scanner->currentToken();
if (_options.isStateVariable && Token::isVariableVisibilitySpecifier(token))
{
+ nodeFactory.markEndPosition();
if (visibility != Declaration::Visibility::Default)
{
parserError(string(
@@ -592,7 +593,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
isDeclaredConst = true;
else if (_options.allowLocationSpecifier && Token::isLocationSpecifier(token))
{
- if (location != VariableDeclaration::Location::Default)
+ if (location != VariableDeclaration::Location::Unspecified)
parserError(string("Location already specified."));
else if (!type)
parserError(string("Location specifier needs explicit type name."));
@@ -616,21 +617,21 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
}
else
break;
+ nodeFactory.markEndPosition();
m_scanner->next();
}
}
- nodeFactory.markEndPosition();
if (_options.allowEmptyName && m_scanner->currentToken() != Token::Identifier)
{
identifier = make_shared<ASTString>("");
solAssert(!_options.allowVar, ""); // allowEmptyName && allowVar makes no sense
- if (type)
- nodeFactory.setEndPositionFromNode(type);
- // if type is null this has already caused an error
}
else
+ {
+ nodeFactory.markEndPosition();
identifier = expectIdentifierToken();
+ }
ASTPointer<Expression> value;
if (_options.allowInitialValue)
{
diff --git a/libsolidity/parsing/Token.cpp b/libsolidity/parsing/Token.cpp
index 5ce74316..27acb7d4 100644
--- a/libsolidity/parsing/Token.cpp
+++ b/libsolidity/parsing/Token.cpp
@@ -63,7 +63,7 @@ void ElementaryTypeNameToken::assertDetails(Token::Value _baseType, unsigned con
{
solAssert(_second == 0, "There should not be a second size argument to type " + string(Token::toString(_baseType)) + ".");
solAssert(
- _first <= 256 && _first % 8 == 0,
+ _first <= 256 && _first % 8 == 0,
"No elementary type " + string(Token::toString(_baseType)) + to_string(_first) + "."
);
}
@@ -165,7 +165,7 @@ tuple<Token::Value, unsigned int, unsigned int> Token::fromIdentifierOrKeyword(s
else
return make_tuple(Token::FixedMxN, m, n);
}
- }
+ }
}
return make_tuple(Token::Identifier, 0, 0);
}