aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/ast
diff options
context:
space:
mode:
authorchriseth <chris@ethereum.org>2018-11-14 02:33:35 +0800
committerGitHub <noreply@github.com>2018-11-14 02:33:35 +0800
commit1d4f565a64988a3400847d2655ca24f73f234bc6 (patch)
treecaaa6c26e307513505349b50ca4f2a8a9506752b /libsolidity/ast
parent59dbf8f1085b8b92e8b7eb0ce380cbeb642e97eb (diff)
parent91b6b8a88e76016e0324036cb7a7f9300a1e2439 (diff)
downloaddexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.gz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.bz2
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.lz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.xz
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.tar.zst
dexon-solidity-1d4f565a64988a3400847d2655ca24f73f234bc6.zip
Merge pull request #5416 from ethereum/develop
Merge develop into release for 0.5.0
Diffstat (limited to 'libsolidity/ast')
-rw-r--r--libsolidity/ast/AST.cpp157
-rw-r--r--libsolidity/ast/AST.h128
-rw-r--r--libsolidity/ast/ASTAnnotations.h7
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp52
-rw-r--r--libsolidity/ast/ASTJsonConverter.h2
-rw-r--r--libsolidity/ast/ASTPrinter.cpp8
-rw-r--r--libsolidity/ast/ASTUtils.cpp48
-rw-r--r--libsolidity/ast/ASTUtils.h54
-rw-r--r--libsolidity/ast/ExperimentalFeatures.h5
-rw-r--r--libsolidity/ast/Types.cpp769
-rw-r--r--libsolidity/ast/Types.h194
11 files changed, 830 insertions, 594 deletions
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 80f5d642..3ae6bd6d 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -24,7 +24,7 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/AST_accept.h>
-#include <libdevcore/SHA3.h>
+#include <libdevcore/Keccak256.h>
#include <boost/algorithm/string.hpp>
@@ -311,8 +311,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
return make_shared<FunctionType>(*this, _internal);
case Declaration::Visibility::External:
return {};
- default:
- solAssert(false, "visibility() should not return a Visibility");
}
}
else
@@ -327,8 +325,6 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const
case Declaration::Visibility::Public:
case Declaration::Visibility::External:
return make_shared<FunctionType>(*this, _internal);
- default:
- solAssert(false, "visibility() should not return a Visibility");
}
}
@@ -347,15 +343,6 @@ string FunctionDefinition::externalSignature() const
return FunctionType(*this).externalSignature();
}
-string FunctionDefinition::fullyQualifiedName() const
-{
- auto const* contract = dynamic_cast<ContractDefinition const*>(scope());
- solAssert(contract, "Enclosing scope of function definition was not set.");
-
- auto fname = name().empty() ? "<fallback>" : name();
- return sourceUnitName() + ":" + contract->name() + "." + fname;
-}
-
FunctionDefinitionAnnotation& FunctionDefinition::annotation() const
{
if (!m_annotation)
@@ -406,7 +393,7 @@ SourceUnit const& Scopable::sourceUnit() const
{
ASTNode const* s = scope();
solAssert(s, "");
- // will not always be a declaratoion
+ // will not always be a declaration
while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
s = dynamic_cast<Scopable const*>(s)->scope();
return dynamic_cast<SourceUnit const&>(*s);
@@ -427,6 +414,7 @@ bool VariableDeclaration::isLocalVariable() const
{
auto s = scope();
return
+ dynamic_cast<FunctionTypeName const*>(s) ||
dynamic_cast<CallableDeclaration const*>(s) ||
dynamic_cast<Block const*>(s) ||
dynamic_cast<ForStatement const*>(s);
@@ -434,14 +422,18 @@ bool VariableDeclaration::isLocalVariable() const
bool VariableDeclaration::isCallableParameter() const
{
- auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
- if (!callable)
- return false;
- for (auto const& variable: callable->parameters())
- if (variable.get() == this)
- return true;
- if (callable->returnParameterList())
- for (auto const& variable: callable->returnParameterList()->parameters())
+ if (isReturnParameter())
+ return true;
+
+ vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
+
+ if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
+ parameters = &funTypeName->parameterTypes();
+ else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
+ parameters = &callable->parameters();
+
+ if (parameters)
+ for (auto const& variable: *parameters)
if (variable.get() == this)
return true;
return false;
@@ -454,11 +446,16 @@ bool VariableDeclaration::isLocalOrReturn() const
bool VariableDeclaration::isReturnParameter() const
{
- auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
- if (!callable)
- return false;
- if (callable->returnParameterList())
- for (auto const& variable: callable->returnParameterList()->parameters())
+ vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr;
+
+ if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
+ returnParameters = &funTypeName->returnParameterTypes();
+ else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
+ if (callable->returnParameterList())
+ returnParameters = &callable->returnParameterList()->parameters();
+
+ if (returnParameters)
+ for (auto const& variable: *returnParameters)
if (variable.get() == this)
return true;
return false;
@@ -466,18 +463,86 @@ bool VariableDeclaration::isReturnParameter() const
bool VariableDeclaration::isExternalCallableParameter() const
{
- auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
- if (!callable || callable->visibility() != Declaration::Visibility::External)
+ if (!isCallableParameter())
return false;
- for (auto const& variable: callable->parameters())
- if (variable.get() == this)
- return true;
+
+ if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
+ if (callable->visibility() == Declaration::Visibility::External)
+ return !isReturnParameter();
+
return false;
}
-bool VariableDeclaration::canHaveAutoType() const
+bool VariableDeclaration::isInternalCallableParameter() const
+{
+ if (!isCallableParameter())
+ return false;
+
+ if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
+ return funTypeName->visibility() == Declaration::Visibility::Internal;
+ else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
+ return callable->visibility() <= Declaration::Visibility::Internal;
+ return false;
+}
+
+bool VariableDeclaration::isLibraryFunctionParameter() const
+{
+ if (!isCallableParameter())
+ return false;
+ if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
+ return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
+ else
+ return false;
+}
+
+bool VariableDeclaration::isEventParameter() const
+{
+ return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
+}
+
+bool VariableDeclaration::hasReferenceOrMappingType() const
{
- return isLocalVariable() && !isCallableParameter();
+ solAssert(typeName(), "");
+ solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
+ TypePointer const& type = typeName()->annotation().type;
+ return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type.get());
+}
+
+set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
+{
+ using Location = VariableDeclaration::Location;
+
+ if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
+ return set<Location>{ Location::Unspecified };
+ else if (isStateVariable() && isConstant())
+ return set<Location>{ Location::Memory };
+ else if (isExternalCallableParameter())
+ {
+ set<Location> locations{ Location::CallData };
+ if (isLibraryFunctionParameter())
+ locations.insert(Location::Storage);
+ return locations;
+ }
+ else if (isCallableParameter())
+ {
+ set<Location> locations{ Location::Memory };
+ if (isInternalCallableParameter() || isLibraryFunctionParameter())
+ locations.insert(Location::Storage);
+ return locations;
+ }
+ else if (isLocalVariable())
+ {
+ solAssert(typeName(), "");
+ solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
+ if (typeName()->annotation().type->category() == Type::Category::Mapping)
+ return set<Location>{ Location::Storage };
+ else
+ // TODO: add Location::Calldata once implemented for local variables.
+ return set<Location>{ Location::Memory, Location::Storage };
+ }
+ else
+ // Struct members etc.
+ return set<Location>{ Location::Unspecified };
}
TypePointer VariableDeclaration::type() const
@@ -499,8 +564,6 @@ FunctionTypePointer VariableDeclaration::functionType(bool _internal) const
case Declaration::Visibility::Public:
case Declaration::Visibility::External:
return make_shared<FunctionType>(*this);
- default:
- solAssert(false, "visibility() should not return a Visibility");
}
// To make the compiler happy
@@ -535,13 +598,6 @@ ReturnAnnotation& Return::annotation() const
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
}
-VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
-{
- if (!m_annotation)
- m_annotation = new VariableDeclarationStatementAnnotation();
- return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
-}
-
ExpressionAnnotation& Expression::annotation() const
{
if (!m_annotation)
@@ -577,6 +633,11 @@ IdentifierAnnotation& Identifier::annotation() const
return dynamic_cast<IdentifierAnnotation&>(*m_annotation);
}
+ASTString Literal::valueWithoutUnderscores() const
+{
+ return boost::erase_all_copy(value(), "_");
+}
+
bool Literal::isHexNumber() const
{
if (token() != Token::Number)
@@ -592,20 +653,20 @@ bool Literal::looksLikeAddress() const
if (!isHexNumber())
return false;
- return abs(int(value().length()) - 42) <= 1;
+ return abs(int(valueWithoutUnderscores().length()) - 42) <= 1;
}
bool Literal::passesAddressChecksum() const
{
solAssert(isHexNumber(), "Expected hex number");
- return dev::passesAddressChecksum(value(), true);
+ return dev::passesAddressChecksum(valueWithoutUnderscores(), true);
}
-std::string Literal::getChecksummedAddress() const
+string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.
- string address = value().substr(2);
+ string address = valueWithoutUnderscores().substr(2);
if (address.length() > 40)
return string();
address.insert(address.begin(), 40 - address.size(), '0');
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index fa0d6921..4fd2bcb8 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -206,7 +206,6 @@ public:
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
bool isVisibleAsLibraryMember() const { return visibility() >= Visibility::Internal; }
- std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
virtual bool isLValue() const { return false; }
virtual bool isPartOfExternalInterface() const { return false; }
@@ -238,7 +237,7 @@ class PragmaDirective: public ASTNode
public:
PragmaDirective(
SourceLocation const& _location,
- std::vector<Token::Value> const& _tokens,
+ std::vector<Token> const& _tokens,
std::vector<ASTString> const& _literals
): ASTNode(_location), m_tokens(_tokens), m_literals(_literals)
{}
@@ -246,13 +245,13 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- std::vector<Token::Value> const& tokens() const { return m_tokens; }
+ std::vector<Token> const& tokens() const { return m_tokens; }
std::vector<ASTString> const& literals() const { return m_literals; }
private:
/// Sequence of tokens following the "pragma" keyword.
- std::vector<Token::Value> m_tokens;
+ std::vector<Token> m_tokens;
/// Sequence of literals following the "pragma" keyword.
std::vector<ASTString> m_literals;
};
@@ -406,6 +405,8 @@ public:
/// Returns the fallback function or nullptr if no fallback function was specified.
FunctionDefinition const* fallbackFunction() const;
+ std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
+
virtual TypePointer type() const override;
virtual ContractDefinitionAnnotation& annotation() const override;
@@ -614,13 +615,11 @@ public:
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
- bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); }
bool isFallback() const { return !m_isConstructor && name().empty(); }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { solAssert(m_body, ""); return *m_body; }
- std::string fullyQualifiedName() const;
virtual bool isVisibleInContract() const override
{
return Declaration::isVisibleInContract() && !isConstructor() && !isFallback();
@@ -656,7 +655,7 @@ private:
class VariableDeclaration: public Declaration
{
public:
- enum Location { Default, Storage, Memory };
+ enum Location { Unspecified, Storage, Memory, CallData };
VariableDeclaration(
SourceLocation const& _sourceLocation,
@@ -667,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),
@@ -686,6 +685,8 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
+ /// @returns true iff this variable is the parameter (or return parameter) of a function
+ /// (or function type name or event) or declared inside a function body.
bool isLocalVariable() const;
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
@@ -694,14 +695,27 @@ public:
/// @returns true if this variable is a local variable or return parameter.
bool isLocalOrReturn() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
+ /// This excludes parameters of external function type names.
bool isExternalCallableParameter() const;
+ /// @returns true if this variable is a parameter or return parameter of an internal function
+ /// or a function type of internal visibility.
+ bool isInternalCallableParameter() const;
+ /// @returns true iff this variable is a parameter(or return parameter of a library function
+ bool isLibraryFunctionParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
/// in the body of a function or modifier.
- bool canHaveAutoType() const;
+ /// @returns true if this variable is a parameter of an event.
+ bool isEventParameter() const;
+ /// @returns true if the type of the variable is a reference or mapping type, i.e.
+ /// array, struct or mapping. These types can take a data location (and often require it).
+ /// Can only be called after reference resolution.
+ bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
+ /// @returns a set of allowed storage locations for the variable.
+ std::set<Location> allowedDataLocations() const;
virtual TypePointer type() const override;
@@ -862,23 +876,31 @@ public:
};
/**
- * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings,
- * contracts, functions, etc.
+ * Any pre-defined type name represented by a single keyword (and possibly a state mutability for address types),
+ * i.e. it excludes mappings, contracts, functions, etc.
*/
class ElementaryTypeName: public TypeName
{
public:
- ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem):
- TypeName(_location), m_type(_elem)
- {}
+ ElementaryTypeName(
+ SourceLocation const& _location,
+ ElementaryTypeNameToken const& _elem,
+ boost::optional<StateMutability> _stateMutability = {}
+ ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability)
+ {
+ solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, "");
+ }
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
ElementaryTypeNameToken const& typeName() const { return m_type; }
+ boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; }
+
private:
ElementaryTypeNameToken m_type;
+ boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type
};
/**
@@ -1169,11 +1191,11 @@ public:
Statement const& body() const { return *m_body; }
private:
- /// For statement's initialization expresion. for(XXX; ; ). Can be empty
+ /// For statement's initialization expression. for (XXX; ; ). Can be empty
ASTPointer<Statement> m_initExpression;
- /// For statement's condition expresion. for(; XXX ; ). Can be empty
+ /// For statement's condition expression. for (; XXX ; ). Can be empty
ASTPointer<Expression> m_condExpression;
- /// For statement's loop expresion. for(;;XXX). Can be empty
+ /// For statement's loop expression. for (;;XXX). Can be empty
ASTPointer<ExpressionStatement> m_loopExpression;
/// The body of the loop
ASTPointer<Statement> m_body;
@@ -1250,13 +1272,12 @@ private:
};
/**
- * Definition of a variable as a statement inside a function. It requires a type name (which can
- * also be "var") but the actual assignment can be missing.
- * Examples: var a = 2; uint256 a;
- * As a second form, multiple variables can be declared, cannot have a type and must be assigned
- * right away. If the first or last component is unnamed, it can "consume" an arbitrary number
- * of components.
- * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d();
+ * Definition of one or more variables as a statement inside a function.
+ * If multiple variables are declared, a value has to be assigned directly.
+ * If only a single variable is declared, the value can be missing.
+ * Examples:
+ * uint[] memory a; uint a = 2;
+ * (uint a, bytes32 b, ) = f(); (, uint a, , StructName storage x) = g();
*/
class VariableDeclarationStatement: public Statement
{
@@ -1271,13 +1292,14 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- VariableDeclarationStatementAnnotation& annotation() const override;
-
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* initialValue() const { return m_initialValue.get(); }
private:
/// List of variables, some of which can be empty pointers (unnamed components).
+ /// Note that the ``m_value`` member of these is unused. Instead, ``m_initialValue``
+ /// below is used, because the initial value can be a single expression assigned
+ /// to all variables.
std::vector<ASTPointer<VariableDeclaration>> m_variables;
/// The assigned expression / initial value.
ASTPointer<Expression> m_initialValue;
@@ -1357,7 +1379,7 @@ public:
Assignment(
SourceLocation const& _location,
ASTPointer<Expression> const& _leftHandSide,
- Token::Value _assignmentOperator,
+ Token _assignmentOperator,
ASTPointer<Expression> const& _rightHandSide
):
Expression(_location),
@@ -1365,18 +1387,18 @@ public:
m_assigmentOperator(_assignmentOperator),
m_rightHandSide(_rightHandSide)
{
- solAssert(Token::isAssignmentOp(_assignmentOperator), "");
+ solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftHandSide() const { return *m_leftHandSide; }
- Token::Value assignmentOperator() const { return m_assigmentOperator; }
+ Token assignmentOperator() const { return m_assigmentOperator; }
Expression const& rightHandSide() const { return *m_rightHandSide; }
private:
ASTPointer<Expression> m_leftHandSide;
- Token::Value m_assigmentOperator;
+ Token m_assigmentOperator;
ASTPointer<Expression> m_rightHandSide;
};
@@ -1419,7 +1441,7 @@ class UnaryOperation: public Expression
public:
UnaryOperation(
SourceLocation const& _location,
- Token::Value _operator,
+ Token _operator,
ASTPointer<Expression> const& _subExpression,
bool _isPrefix
):
@@ -1428,17 +1450,17 @@ public:
m_subExpression(_subExpression),
m_isPrefix(_isPrefix)
{
- solAssert(Token::isUnaryOp(_operator), "");
+ solAssert(TokenTraits::isUnaryOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- Token::Value getOperator() const { return m_operator; }
+ Token getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; }
Expression const& subExpression() const { return *m_subExpression; }
private:
- Token::Value m_operator;
+ Token m_operator;
ASTPointer<Expression> m_subExpression;
bool m_isPrefix;
};
@@ -1453,25 +1475,25 @@ public:
BinaryOperation(
SourceLocation const& _location,
ASTPointer<Expression> const& _left,
- Token::Value _operator,
+ Token _operator,
ASTPointer<Expression> const& _right
):
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
{
- solAssert(Token::isBinaryOp(_operator) || Token::isCompareOp(_operator), "");
+ solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
Expression const& leftExpression() const { return *m_left; }
Expression const& rightExpression() const { return *m_right; }
- Token::Value getOperator() const { return m_operator; }
+ Token getOperator() const { return m_operator; }
BinaryOperationAnnotation& annotation() const override;
private:
ASTPointer<Expression> m_left;
- Token::Value m_operator;
+ Token m_operator;
ASTPointer<Expression> m_right;
};
@@ -1631,21 +1653,21 @@ class Literal: public PrimaryExpression
public:
enum class SubDenomination
{
- None = Token::Illegal,
- Wei = Token::SubWei,
- Szabo = Token::SubSzabo,
- Finney = Token::SubFinney,
- Ether = Token::SubEther,
- Second = Token::SubSecond,
- Minute = Token::SubMinute,
- Hour = Token::SubHour,
- Day = Token::SubDay,
- Week = Token::SubWeek,
- Year = Token::SubYear
+ None = static_cast<int>(Token::Illegal),
+ Wei = static_cast<int>(Token::SubWei),
+ Szabo = static_cast<int>(Token::SubSzabo),
+ Finney = static_cast<int>(Token::SubFinney),
+ Ether = static_cast<int>(Token::SubEther),
+ Second = static_cast<int>(Token::SubSecond),
+ Minute = static_cast<int>(Token::SubMinute),
+ Hour = static_cast<int>(Token::SubHour),
+ Day = static_cast<int>(Token::SubDay),
+ Week = static_cast<int>(Token::SubWeek),
+ Year = static_cast<int>(Token::SubYear)
};
Literal(
SourceLocation const& _location,
- Token::Value _token,
+ Token _token,
ASTPointer<ASTString> const& _value,
SubDenomination _sub = SubDenomination::None
):
@@ -1653,10 +1675,12 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- Token::Value token() const { return m_token; }
+ Token token() const { return m_token; }
/// @returns the non-parsed value of the literal
ASTString const& value() const { return *m_value; }
+ ASTString valueWithoutUnderscores() const;
+
SubDenomination subDenomination() const { return m_subDenomination; }
/// @returns true if this is a number with a hex prefix.
@@ -1670,7 +1694,7 @@ public:
std::string getChecksummedAddress() const;
private:
- Token::Value m_token;
+ Token m_token;
ASTPointer<ASTString> m_value;
SubDenomination m_subDenomination;
};
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 5cbe42bd..e0b3f492 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -164,13 +164,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
ContractDefinition const* contractScope = nullptr;
};
-struct VariableDeclarationStatementAnnotation: StatementAnnotation
-{
- /// Information about which component of the value is assigned to which variable.
- /// The pointer can be null to signify that the component is discarded.
- std::vector<VariableDeclaration const*> assignments;
-};
-
struct ExpressionAnnotation: ASTAnnotation
{
/// Inferred type of the expression.
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index b8e00b60..2d26ce8a 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -126,7 +126,7 @@ string ASTJsonConverter::sourceLocationToString(SourceLocation const& _location)
int length = -1;
if (_location.start >= 0 && _location.end >= 0)
length = _location.end - _location.start;
- return std::to_string(_location.start) + ":" + std::to_string(length) + ":" + std::to_string(sourceIndex);
+ return to_string(_location.start) + ":" + to_string(length) + ":" + to_string(sourceIndex);
}
string ASTJsonConverter::namePathToString(std::vector<ASTString> const& _namePath)
@@ -325,20 +325,19 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.name()),
make_pair("documentation", _node.documentation() ? Json::Value(*_node.documentation()) : Json::nullValue),
- // FIXME: remove with next breaking release
- make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
- make_pair("payable", _node.isPayable()),
+ make_pair("kind", _node.isConstructor() ? "constructor" : (_node.isFallback() ? "fallback" : "function")),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
make_pair("superFunction", idOrNull(_node.annotation().superFunction)),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("parameters", toJson(_node.parameterList())),
- make_pair("isConstructor", _node.isConstructor()),
make_pair("returnParameters", toJson(*_node.returnParameterList())),
make_pair("modifiers", toJson(_node.modifiers())),
make_pair("body", _node.isImplemented() ? toJson(_node.body()) : Json::nullValue),
make_pair("implemented", _node.isImplemented()),
make_pair("scope", idOrNull(_node.scope()))
};
+ if (m_legacy)
+ attributes.emplace_back("isConstructor", _node.isConstructor());
setJsonNode(_node, "FunctionDefinition", std::move(attributes));
return false;
}
@@ -397,10 +396,15 @@ bool ASTJsonConverter::visit(EventDefinition const& _node)
bool ASTJsonConverter::visit(ElementaryTypeName const& _node)
{
- setJsonNode(_node, "ElementaryTypeName", {
+ std::vector<pair<string, Json::Value>> attributes = {
make_pair("name", _node.typeName().toString()),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
- });
+ };
+
+ if (_node.stateMutability())
+ attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability())));
+
+ setJsonNode(_node, "ElementaryTypeName", std::move(attributes));
return false;
}
@@ -418,11 +422,8 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node)
bool ASTJsonConverter::visit(FunctionTypeName const& _node)
{
setJsonNode(_node, "FunctionTypeName", {
- make_pair("payable", _node.isPayable()),
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())),
- // FIXME: remove with next breaking release
- make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true))
@@ -458,7 +459,7 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
if (it.first)
{
Json::Value tuple(Json::objectValue);
- tuple[it.first->name] = inlineAssemblyIdentifierToJson(it);
+ tuple[it.first->name.str()] = inlineAssemblyIdentifierToJson(it);
externalReferences.append(tuple);
}
}
@@ -555,8 +556,8 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
Json::Value varDecs(Json::arrayValue);
- for (auto const& v: _node.annotation().assignments)
- appendMove(varDecs, idOrNull(v));
+ for (auto const& v: _node.declarations())
+ appendMove(varDecs, idOrNull(v.get()));
setJsonNode(_node, "VariableDeclarationStatement", {
make_pair("assignments", std::move(varDecs)),
make_pair("declarations", toJson(_node.declarations())),
@@ -588,7 +589,7 @@ bool ASTJsonConverter::visit(Conditional const& _node)
bool ASTJsonConverter::visit(Assignment const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
- make_pair("operator", Token::toString(_node.assignmentOperator())),
+ make_pair("operator", TokenTraits::toString(_node.assignmentOperator())),
make_pair("leftHandSide", toJson(_node.leftHandSide())),
make_pair("rightHandSide", toJson(_node.rightHandSide()))
};
@@ -612,7 +613,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
make_pair("prefix", _node.isPrefixOperation()),
- make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("operator", TokenTraits::toString(_node.getOperator())),
make_pair("subExpression", toJson(_node.subExpression()))
};
appendExpressionAttributes(attributes, _node.annotation());
@@ -623,7 +624,7 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
bool ASTJsonConverter::visit(BinaryOperation const& _node)
{
std::vector<pair<string, Json::Value>> attributes = {
- make_pair("operator", Token::toString(_node.getOperator())),
+ make_pair("operator", TokenTraits::toString(_node.getOperator())),
make_pair("leftExpression", toJson(_node.leftExpression())),
make_pair("rightExpression", toJson(_node.rightExpression())),
make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
@@ -718,7 +719,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
Json::Value value{_node.value()};
if (!dev::validateUTF8(_node.value()))
value = Json::nullValue;
- Token::Value subdenomination = Token::Value(_node.subDenomination());
+ Token subdenomination = Token(_node.subDenomination());
std::vector<pair<string, Json::Value>> attributes = {
make_pair(m_legacy ? "token" : "kind", literalTokenKind(_node.token())),
make_pair("value", value),
@@ -727,7 +728,7 @@ bool ASTJsonConverter::visit(Literal const& _node)
"subdenomination",
subdenomination == Token::Illegal ?
Json::nullValue :
- Json::Value{Token::toString(subdenomination)}
+ Json::Value{TokenTraits::toString(subdenomination)}
)
};
appendExpressionAttributes(attributes, _node.annotation());
@@ -745,15 +746,17 @@ string ASTJsonConverter::location(VariableDeclaration::Location _location)
{
switch (_location)
{
- case VariableDeclaration::Location::Default:
+ case VariableDeclaration::Location::Unspecified:
return "default";
case VariableDeclaration::Location::Storage:
return "storage";
case VariableDeclaration::Location::Memory:
return "memory";
- default:
- solAssert(false, "Unknown declaration location.");
+ case VariableDeclaration::Location::CallData:
+ return "calldata";
}
+ // To make the compiler happy
+ return {};
}
string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
@@ -766,9 +769,10 @@ string ASTJsonConverter::contractKind(ContractDefinition::ContractKind _kind)
return "contract";
case ContractDefinition::ContractKind::Library:
return "library";
- default:
- solAssert(false, "Unknown kind of contract.");
}
+
+ // To make the compiler happy
+ return {};
}
string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
@@ -786,7 +790,7 @@ string ASTJsonConverter::functionCallKind(FunctionCallKind _kind)
}
}
-string ASTJsonConverter::literalTokenKind(Token::Value _token)
+string ASTJsonConverter::literalTokenKind(Token _token)
{
switch (_token)
{
diff --git a/libsolidity/ast/ASTJsonConverter.h b/libsolidity/ast/ASTJsonConverter.h
index 29712f3b..8429708c 100644
--- a/libsolidity/ast/ASTJsonConverter.h
+++ b/libsolidity/ast/ASTJsonConverter.h
@@ -134,7 +134,7 @@ private:
static std::string location(VariableDeclaration::Location _location);
static std::string contractKind(ContractDefinition::ContractKind _kind);
static std::string functionCallKind(FunctionCallKind _kind);
- static std::string literalTokenKind(Token::Value _token);
+ static std::string literalTokenKind(Token _token);
static std::string type(Expression const& _expression);
static std::string type(VariableDeclaration const& _varDecl);
static int nodeId(ASTNode const& _node)
diff --git a/libsolidity/ast/ASTPrinter.cpp b/libsolidity/ast/ASTPrinter.cpp
index 4273f225..255cb9be 100644
--- a/libsolidity/ast/ASTPrinter.cpp
+++ b/libsolidity/ast/ASTPrinter.cpp
@@ -289,7 +289,7 @@ bool ASTPrinter::visit(Conditional const& _node)
bool ASTPrinter::visit(Assignment const& _node)
{
- writeLine(string("Assignment using operator ") + Token::toString(_node.assignmentOperator()));
+ writeLine(string("Assignment using operator ") + TokenTraits::toString(_node.assignmentOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -306,7 +306,7 @@ bool ASTPrinter::visit(TupleExpression const& _node)
bool ASTPrinter::visit(UnaryOperation const& _node)
{
writeLine(string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
- ") " + Token::toString(_node.getOperator()));
+ ") " + TokenTraits::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -314,7 +314,7 @@ bool ASTPrinter::visit(UnaryOperation const& _node)
bool ASTPrinter::visit(BinaryOperation const& _node)
{
- writeLine(string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
+ writeLine(string("BinaryOperation using operator ") + TokenTraits::toString(_node.getOperator()));
printType(_node);
printSourcePart(_node);
return goDeeper();
@@ -370,7 +370,7 @@ bool ASTPrinter::visit(ElementaryTypeNameExpression const& _node)
bool ASTPrinter::visit(Literal const& _node)
{
- char const* tokenString = Token::toString(_node.token());
+ char const* tokenString = TokenTraits::toString(_node.token());
if (!tokenString)
tokenString = "[no token]";
writeLine(string("Literal, token: ") + tokenString + " value: " + _node.value());
diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp
deleted file mode 100644
index 7d180490..00000000
--- a/libsolidity/ast/ASTUtils.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- This file is part of solidity.
-
- solidity is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- solidity is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with solidity. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2015
- * Utilities to work with the AST.
- */
-
-#include <libsolidity/ast/ASTUtils.h>
-
-using namespace std;
-using namespace dev;
-using namespace dev::solidity;
-
-
-
-ASTNode const* LocationFinder::leastUpperBound()
-{
- m_bestMatch = nullptr;
- for (ASTNode const* rootNode: m_rootNodes)
- rootNode->accept(*this);
-
- return m_bestMatch;
-}
-
-bool LocationFinder::visitNode(const ASTNode& _node)
-{
- if (_node.location().contains(m_location))
- {
- m_bestMatch = &_node;
- return true;
- }
- return false;
-}
diff --git a/libsolidity/ast/ASTUtils.h b/libsolidity/ast/ASTUtils.h
deleted file mode 100644
index f7cacf3e..00000000
--- a/libsolidity/ast/ASTUtils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- This file is part of solidity.
-
- solidity is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- solidity is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with solidity. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Christian <c@ethdev.com>
- * @date 2015
- * Utilities to work with the AST.
- */
-
-#pragma once
-
-#include <libevmasm/SourceLocation.h>
-#include <libsolidity/ast/ASTVisitor.h>
-
-namespace dev
-{
-namespace solidity
-{
-
-class LocationFinder: private ASTConstVisitor
-{
-public:
- LocationFinder(SourceLocation const& _location, std::vector<ASTNode const*> _rootNodes):
- m_rootNodes(_rootNodes), m_location(_location)
- {
- }
-
- /// @returns the "closest" (in the sense of most-leafward) AST node which is a descendant of
- /// _node and whose source location contains _location.
- ASTNode const* leastUpperBound();
-
-private:
- bool visitNode(ASTNode const& _node);
-
- std::vector<ASTNode const*> m_rootNodes;
- SourceLocation m_location;
- ASTNode const* m_bestMatch = nullptr;
-};
-
-}
-}
diff --git a/libsolidity/ast/ExperimentalFeatures.h b/libsolidity/ast/ExperimentalFeatures.h
index 30ea7ec5..54aad573 100644
--- a/libsolidity/ast/ExperimentalFeatures.h
+++ b/libsolidity/ast/ExperimentalFeatures.h
@@ -29,9 +29,8 @@ namespace solidity
enum class ExperimentalFeature
{
- ABIEncoderV2, // new ABI encoder that makes use of JULIA
+ ABIEncoderV2, // new ABI encoder that makes use of Yul
SMTChecker,
- V050, // v0.5.0 breaking changes
Test,
TestOnlyAnalysis
};
@@ -40,14 +39,12 @@ static const std::map<ExperimentalFeature, bool> ExperimentalFeatureOnlyAnalysis
{
{ ExperimentalFeature::SMTChecker, true },
{ ExperimentalFeature::TestOnlyAnalysis, true },
- { ExperimentalFeature::V050, true }
};
static const std::map<std::string, ExperimentalFeature> ExperimentalFeatureNames =
{
{ "ABIEncoderV2", ExperimentalFeature::ABIEncoderV2 },
{ "SMTChecker", ExperimentalFeature::SMTChecker },
- { "v0.5.0", ExperimentalFeature::V050 },
{ "__test", ExperimentalFeature::Test },
{ "__testOnlyAnalysis", ExperimentalFeature::TestOnlyAnalysis },
};
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 60e3183c..4b31d2e8 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -26,17 +26,20 @@
#include <libdevcore/CommonIO.h>
#include <libdevcore/CommonData.h>
-#include <libdevcore/SHA3.h>
+#include <libdevcore/Keccak256.h>
#include <libdevcore/UTF8.h>
#include <libdevcore/Algorithms.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/classification.hpp>
+#include <boost/algorithm/string/split.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/adaptor/sliced.hpp>
#include <boost/range/adaptor/transformed.hpp>
+#include <boost/algorithm/string.hpp>
#include <limits>
@@ -167,15 +170,6 @@ pair<u256, unsigned> const* StorageOffsets::offset(size_t _index) const
return nullptr;
}
-MemberList& MemberList::operator=(MemberList&& _other)
-{
- solAssert(&_other != this, "");
-
- m_memberTypes = move(_other.m_memberTypes);
- m_storageOffsets = move(_other.m_storageOffsets);
- return *this;
-}
-
void MemberList::combine(MemberList const & _other)
{
m_memberTypes += _other.m_memberTypes;
@@ -261,13 +255,24 @@ string Type::escapeIdentifier(string const& _identifier)
return ret;
}
+string Type::identifier() const
+{
+ string ret = escapeIdentifier(richIdentifier());
+ solAssert(ret.find_first_of("0123456789") != 0, "Identifier cannot start with a number.");
+ solAssert(
+ ret.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMONPQRSTUVWXYZ_$") == string::npos,
+ "Identifier contains invalid characters."
+ );
+ return ret;
+}
+
TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
{
- solAssert(Token::isElementaryTypeName(_type.token()),
+ solAssert(TokenTraits::isElementaryTypeName(_type.token()),
"Expected an elementary type name but got " + _type.toString()
);
- Token::Value token = _type.token();
+ Token token = _type.token();
unsigned m = _type.firstNumber();
unsigned n = _type.secondNumber();
@@ -294,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>(StateMutability::NonPayable);
case Token::Bool:
return make_shared<BoolType>();
case Token::Bytes:
@@ -312,22 +317,45 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
TypePointer Type::fromElementaryTypeName(string const& _name)
{
- string name = _name;
- DataLocation location = DataLocation::Storage;
- if (boost::algorithm::ends_with(name, " memory"))
- {
- name = name.substr(0, name.length() - 7);
- location = DataLocation::Memory;
- }
- unsigned short firstNum;
- unsigned short secondNum;
- Token::Value token;
- tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name);
+ vector<string> nameParts;
+ boost::split(nameParts, _name, boost::is_any_of(" "));
+ solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name);
+ Token token;
+ unsigned short firstNum, secondNum;
+ tie(token, firstNum, secondNum) = TokenTraits::fromIdentifierOrKeyword(nameParts[0]);
auto t = fromElementaryTypeName(ElementaryTypeNameToken(token, firstNum, secondNum));
if (auto* ref = dynamic_cast<ReferenceType const*>(t.get()))
+ {
+ DataLocation location = DataLocation::Storage;
+ if (nameParts.size() == 2)
+ {
+ if (nameParts[1] == "storage")
+ location = DataLocation::Storage;
+ else if (nameParts[1] == "calldata")
+ location = DataLocation::CallData;
+ else if (nameParts[1] == "memory")
+ location = DataLocation::Memory;
+ else
+ solAssert(false, "Unknown data location: " + nameParts[1]);
+ }
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");
return t;
+ }
}
TypePointer Type::forLiteral(Literal const& _literal)
@@ -338,13 +366,7 @@ TypePointer Type::forLiteral(Literal const& _literal)
case Token::FalseLiteral:
return make_shared<BoolType>();
case Token::Number:
- {
- tuple<bool, rational> validLiteral = RationalNumberType::isValidLiteral(_literal);
- if (get<0>(validLiteral) == true)
- return make_shared<RationalNumberType>(get<1>(validLiteral));
- else
- return TypePointer();
- }
+ return RationalNumberType::forLiteral(_literal);
case Token::StringLiteral:
return make_shared<StringLiteralType>(_literal);
default:
@@ -376,6 +398,27 @@ MemberList const& Type::members(ContractDefinition const* _currentScope) const
return *m_members[_currentScope];
}
+TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const
+{
+ TypePointer encodingType = mobileType();
+ if (encodingType)
+ encodingType = encodingType->interfaceType(_inLibraryCall);
+ if (encodingType)
+ encodingType = encodingType->encodingType();
+ // Structs are fine in the following circumstances:
+ // - ABIv2 without packed encoding or,
+ // - storage struct for a library
+ if (_inLibraryCall && encodingType->dataStoredIn(DataLocation::Storage))
+ return encodingType;
+ TypePointer baseType = encodingType;
+ while (auto const* arrayType = dynamic_cast<ArrayType const*>(baseType.get()))
+ baseType = arrayType->baseType();
+ if (dynamic_cast<StructType const*>(baseType.get()))
+ if (!_encoderV2 || _packed)
+ return TypePointer();
+ return encodingType;
+}
+
MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
{
// Normalise data location of type.
@@ -407,16 +450,108 @@ 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
+{
+ 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::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";
+}
+
+u256 AddressType::literalValue(Literal const* _literal) const
+{
+ solAssert(_literal, "");
+ solAssert(_literal->value().substr(0, 2) == "0x", "");
+ return u256(_literal->valueWithoutUnderscores());
+}
+
+TypePointer AddressType::unaryOperatorResult(Token _operator) const
+{
+ return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
+}
+
+
+TypePointer AddressType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
+{
+ // Addresses can only be compared.
+ if (!TokenTraits::isCompareOp(_operator))
+ return TypePointer();
+
+ 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
+{
+ 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)},
+ {"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
{
-bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountType)
+bool isValidShiftAndAmountType(Token _operator, Type const& _shiftAmountType)
{
// Disable >>> here.
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
@@ -428,8 +563,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)
@@ -438,10 +571,7 @@ IntegerType::IntegerType(unsigned _bits, IntegerType::Modifier _modifier):
string IntegerType::richIdentifier() const
{
- if (isAddress())
- return "t_address";
- else
- return "t_" + string(isSigned() ? "" : "u") + "int" + std::to_string(numBits());
+ return "t_" + string(isSigned() ? "" : "u") + "int" + to_string(numBits());
}
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@@ -451,8 +581,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
@@ -461,11 +589,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;
@@ -474,21 +598,19 @@ 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 ||
+ (_convertTo.category() == Category::FixedBytes && numBits() == dynamic_cast<FixedBytesType const&>(_convertTo).numBytes() * 8) ||
_convertTo.category() == Category::FixedPoint;
}
-TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const
+TypePointer IntegerType::unaryOperatorResult(Token _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)
@@ -507,20 +629,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())
@@ -537,7 +649,7 @@ bigint IntegerType::maxValue() const
return (bigint(1) << m_bits) - 1;
}
-TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer IntegerType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (
_other->category() != Category::RationalNumber &&
@@ -545,31 +657,26 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe
_other->category() != category()
)
return TypePointer();
- if (Token::isShiftOp(_operator))
+ if (TokenTraits::isShiftOp(_operator))
{
// Shifts are not symmetric with respect to the type
- if (isAddress())
- return TypePointer();
if (isValidShiftAndAmountType(_operator, *_other))
return shared_from_this();
else
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();
// All integer types can be compared
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
return commonType;
- if (Token::isBooleanOp(_operator))
+ if (TokenTraits::isBooleanOp(_operator))
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();
@@ -580,21 +687,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(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
- {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
- {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
- {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
- {"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)
{
@@ -607,7 +699,7 @@ FixedPointType::FixedPointType(unsigned _totalBits, unsigned _fractionalDigits,
string FixedPointType::richIdentifier() const
{
- return "t_" + string(isSigned() ? "" : "u") + "fixed" + std::to_string(m_totalBits) + "x" + std::to_string(m_fractionalDigits);
+ return "t_" + string(isSigned() ? "" : "u") + "fixed" + to_string(m_totalBits) + "x" + to_string(m_fractionalDigits);
}
bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const
@@ -625,11 +717,10 @@ 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
+TypePointer FixedPointType::unaryOperatorResult(Token _operator) const
{
switch(_operator)
{
@@ -664,7 +755,7 @@ string FixedPointType::toString(bool) const
bigint FixedPointType::maxIntegerValue() const
{
bigint maxValue = (bigint(1) << (m_totalBits - (isSigned() ? 1 : 0))) - 1;
- return maxValue / pow(bigint(10), m_fractionalDigits);
+ return maxValue / boost::multiprecision::pow(bigint(10), m_fractionalDigits);
}
bigint FixedPointType::minIntegerValue() const
@@ -672,13 +763,13 @@ bigint FixedPointType::minIntegerValue() const
if (isSigned())
{
bigint minValue = -(bigint(1) << (m_totalBits - (isSigned() ? 1 : 0)));
- return minValue / pow(bigint(10), m_fractionalDigits);
+ return minValue / boost::multiprecision::pow(bigint(10), m_fractionalDigits);
}
else
return bigint(0);
}
-TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FixedPointType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
auto commonType = Type::commonType(shared_from_this(), _other);
@@ -686,9 +777,9 @@ TypePointer FixedPointType::binaryOperatorResult(Token::Value _operator, TypePoi
return TypePointer();
// All fixed types can be compared
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
return commonType;
- if (Token::isBitOp(_operator) || Token::isBooleanOp(_operator) || _operator == Token::Exp)
+ if (TokenTraits::isBitOp(_operator) || TokenTraits::isBooleanOp(_operator) || _operator == Token::Exp)
return TypePointer();
return commonType;
}
@@ -741,36 +832,60 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
}
}
+TypePointer RationalNumberType::forLiteral(Literal const& _literal)
+{
+ solAssert(_literal.token() == Token::Number, "");
+ tuple<bool, rational> validLiteral = isValidLiteral(_literal);
+ if (get<0>(validLiteral))
+ {
+ TypePointer compatibleBytesType;
+ if (_literal.isHexNumber())
+ {
+ size_t const digitCount = _literal.valueWithoutUnderscores().length() - 2;
+ if (digitCount % 2 == 0 && (digitCount / 2) <= 32)
+ compatibleBytesType = make_shared<FixedBytesType>(digitCount / 2);
+ }
+
+ return make_shared<RationalNumberType>(get<1>(validLiteral), compatibleBytesType);
+ }
+ return TypePointer();
+}
+
tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal)
{
rational value;
try
{
- auto expPoint = find(_literal.value().begin(), _literal.value().end(), 'e');
- if (expPoint == _literal.value().end())
- expPoint = find(_literal.value().begin(), _literal.value().end(), 'E');
+ ASTString valueString = _literal.valueWithoutUnderscores();
+
+ auto expPoint = find(valueString.begin(), valueString.end(), 'e');
+ if (expPoint == valueString.end())
+ expPoint = find(valueString.begin(), valueString.end(), 'E');
- if (boost::starts_with(_literal.value(), "0x"))
+ if (boost::starts_with(valueString, "0x"))
{
// process as hex
- value = bigint(_literal.value());
+ value = bigint(valueString);
}
- else if (expPoint != _literal.value().end())
+ else if (expPoint != valueString.end())
{
- // Parse base and exponent. Checks numeric limit.
- bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
+ // Parse mantissa and exponent. Checks numeric limit.
+ tuple<bool, rational> mantissa = parseRational(string(valueString.begin(), expPoint));
- if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
+ if (!get<0>(mantissa))
return make_tuple(false, rational(0));
+ value = get<1>(mantissa);
- uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
-
+ // 0E... is always zero.
+ if (value == 0)
+ return make_tuple(true, rational(0));
- tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint));
+ bigint exp = bigint(string(expPoint + 1, valueString.end()));
- if (!get<0>(base))
+ if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
return make_tuple(false, rational(0));
- value = get<1>(base);
+
+ uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
if (exp < 0)
{
@@ -794,7 +909,7 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
else
{
// parse as rational number
- tuple<bool, rational> tmp = parseRational(_literal.value());
+ tuple<bool, rational> tmp = parseRational(valueString);
if (!get<0>(tmp))
return tmp;
value = get<1>(tmp);
@@ -842,52 +957,56 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
bool RationalNumberType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
- if (_convertTo.category() == Category::Integer)
+ switch (_convertTo.category())
+ {
+ case Category::Integer:
{
- if (m_value == rational(0))
- return true;
if (isFractional())
return false;
IntegerType const& targetType = dynamic_cast<IntegerType const&>(_convertTo);
+ if (m_value == rational(0))
+ return true;
unsigned forSignBit = (targetType.isSigned() ? 1 : 0);
if (m_value > rational(0))
{
if (m_value.numerator() <= (u256(-1) >> (256 - targetType.numBits() + forSignBit)))
return true;
+ return false;
+ }
+ if (targetType.isSigned())
+ {
+ if (-m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit)))
+ return true;
}
- else if (targetType.isSigned() && -m_value.numerator() <= (u256(1) << (targetType.numBits() - forSignBit)))
- return true;
return false;
}
- else if (_convertTo.category() == Category::FixedPoint)
+ case Category::FixedPoint:
{
if (auto fixed = fixedPointType())
return fixed->isImplicitlyConvertibleTo(_convertTo);
- else
- return false;
+ return false;
}
- else if (_convertTo.category() == Category::FixedBytes)
- {
- FixedBytesType const& fixedBytes = dynamic_cast<FixedBytesType const&>(_convertTo);
- if (!isFractional())
- {
- if (integerType())
- return fixedBytes.numBytes() * 8 >= integerType()->numBits();
- return false;
- }
- else
- return false;
+ case Category::FixedBytes:
+ return (m_value == rational(0)) || (m_compatibleBytesType && *m_compatibleBytesType == _convertTo);
+ default:
+ return false;
}
- return false;
}
bool RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- TypePointer mobType = mobileType();
- return mobType && mobType->isExplicitlyConvertibleTo(_convertTo);
+ if (isImplicitlyConvertibleTo(_convertTo))
+ return true;
+ else if (_convertTo.category() != Category::FixedBytes)
+ {
+ TypePointer mobType = mobileType();
+ return (mobType && mobType->isExplicitlyConvertibleTo(_convertTo));
+ }
+ else
+ return false;
}
-TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) const
+TypePointer RationalNumberType::unaryOperatorResult(Token _operator) const
{
rational value;
switch (_operator)
@@ -911,7 +1030,7 @@ TypePointer RationalNumberType::unaryOperatorResult(Token::Value _operator) cons
return make_shared<RationalNumberType>(value);
}
-TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer RationalNumberType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() == Category::Integer || _other->category() == Category::FixedPoint)
{
@@ -924,9 +1043,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
return TypePointer();
RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other);
- if (Token::isCompareOp(_operator))
+ if (TokenTraits::isCompareOp(_operator))
{
- // Since we do not have a "BoolConstantType", we have to do the acutal comparison
+ // Since we do not have a "BoolConstantType", we have to do the actual comparison
// at runtime and convert to mobile typse first. Such a comparison is not a very common
// use-case and will be optimized away.
TypePointer thisMobile = mobileType();
@@ -982,10 +1101,9 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
}
else
value = m_value.numerator() % other.m_value.numerator();
- break;
+ break;
case Token::Exp:
{
- using boost::multiprecision::pow;
if (other.isFractional())
return TypePointer();
solAssert(other.m_value.denominator() == 1, "");
@@ -1019,23 +1137,22 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
else if (_base == -1)
return 1 - 2 * int(_exponent & 1);
else
- return pow(_base, _exponent);
+ return boost::multiprecision::pow(_base, _exponent);
};
bigint numerator = optimizedPow(m_value.numerator(), absExp);
bigint denominator = optimizedPow(m_value.denominator(), absExp);
if (exp >= 0)
- value = rational(numerator, denominator);
+ value = makeRational(numerator, denominator);
else
// invert
- value = rational(denominator, numerator);
+ value = makeRational(denominator, numerator);
}
break;
}
case Token::SHL:
{
- using boost::multiprecision::pow;
if (fractional)
return TypePointer();
else if (other.m_value < 0)
@@ -1049,7 +1166,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent))
return TypePointer();
- value = m_value.numerator() * pow(bigint(2), exponent);
+ value = m_value.numerator() * boost::multiprecision::pow(bigint(2), exponent);
}
break;
}
@@ -1057,7 +1174,6 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
// determines the resulting type and the type of shift (SAR or SHR).
case Token::SAR:
{
- namespace mp = boost::multiprecision;
if (fractional)
return TypePointer();
else if (other.m_value < 0)
@@ -1069,10 +1185,22 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
else
{
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
- if (exponent > mostSignificantBit(mp::abs(m_value.numerator())))
- value = 0;
+ if (exponent > mostSignificantBit(boost::multiprecision::abs(m_value.numerator())))
+ value = m_value.numerator() < 0 ? -1 : 0;
else
- value = rational(m_value.numerator() / mp::pow(bigint(2), exponent), 1);
+ {
+ if (m_value.numerator() < 0)
+ // Add 1 to the negative value before dividing to get a result that is strictly too large,
+ // then subtract 1 afterwards to round towards negative infinity.
+ // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...).
+ // To see this note that for negative x, xor(x,all_ones) = (-x-1) and
+ // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is
+ // -(-x - 1) / 2^shift_amount - 1, which is the same as
+ // (x + 1) / 2^shift_amount - 1.
+ value = rational((m_value.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1);
+ else
+ value = rational(m_value.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1);
+ }
}
break;
}
@@ -1090,7 +1218,14 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
string RationalNumberType::richIdentifier() const
{
- return "t_rational_" + m_value.numerator().str() + "_by_" + m_value.denominator().str();
+ // rational seemingly will put the sign always on the numerator,
+ // but let just make it deterministic here.
+ bigint numerator = abs(m_value.numerator());
+ bigint denominator = abs(m_value.denominator());
+ if (m_value < 0)
+ return "t_rational_minus_" + numerator.str() + "_by_" + denominator.str();
+ else
+ return "t_rational_" + numerator.str() + "_by_" + denominator.str();
}
bool RationalNumberType::operator==(Type const& _other) const
@@ -1128,20 +1263,20 @@ u256 RationalNumberType::literalValue(Literal const*) const
// its value.
u256 value;
- bigint shiftedValue;
+ bigint shiftedValue;
if (!isFractional())
shiftedValue = m_value.numerator();
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() * pow(bigint(10), fractionalDigits) / m_value.denominator();
+ 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))
@@ -1180,7 +1315,7 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
bool negative = (m_value < 0);
unsigned fractionalDigits = 0;
rational value = abs(m_value); // We care about the sign later.
- rational maxValue = negative ?
+ rational maxValue = negative ?
rational(bigint(1) << 255, 1):
rational((bigint(1) << 256) - 1, 1);
@@ -1189,12 +1324,13 @@ shared_ptr<FixedPointType const> RationalNumberType::fixedPointType() const
value *= 10;
fractionalDigits++;
}
-
+
if (value > maxValue)
return shared_ptr<FixedPointType const>();
// This means we round towards zero for positive and negative values.
bigint v = value.numerator() / value.denominator();
- if (negative)
+
+ if (negative && v != 0)
// modify value to satisfy bit requirements for negative numbers:
// add one bit for sign and decrement because negative numbers can be larger
v = (v - 1) << 1;
@@ -1281,12 +1417,13 @@ bool FixedBytesType::isImplicitlyConvertibleTo(Type const& _convertTo) const
bool FixedBytesType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return _convertTo.category() == Category::Integer ||
+ 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();
}
-TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
+TypePointer FixedBytesType::unaryOperatorResult(Token _operator) const
{
// "delete" and "~" is okay for FixedBytesType
if (_operator == Token::Delete)
@@ -1297,9 +1434,9 @@ TypePointer FixedBytesType::unaryOperatorResult(Token::Value _operator) const
return TypePointer();
}
-TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FixedBytesType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- if (Token::isShiftOp(_operator))
+ if (TokenTraits::isShiftOp(_operator))
{
if (isValidShiftAndAmountType(_operator, *_other))
return shared_from_this();
@@ -1312,7 +1449,7 @@ TypePointer FixedBytesType::binaryOperatorResult(Token::Value _operator, TypePoi
return TypePointer();
// FixedBytes can be compared and have bitwise operators applied to them
- if (Token::isCompareOp(_operator) || Token::isBitOp(_operator))
+ if (TokenTraits::isCompareOp(_operator) || TokenTraits::isBitOp(_operator))
return commonType;
return TypePointer();
@@ -1325,7 +1462,7 @@ MemberList::MemberMap FixedBytesType::nativeMembers(const ContractDefinition*) c
string FixedBytesType::richIdentifier() const
{
- return "t_bytes" + std::to_string(m_bytes);
+ return "t_bytes" + to_string(m_bytes);
}
bool FixedBytesType::operator==(Type const& _other) const
@@ -1347,18 +1484,18 @@ u256 BoolType::literalValue(Literal const* _literal) const
solAssert(false, "Bool type constructed from non-boolean literal.");
}
-TypePointer BoolType::unaryOperatorResult(Token::Value _operator) const
+TypePointer BoolType::unaryOperatorResult(Token _operator) const
{
if (_operator == Token::Delete)
return make_shared<TupleType>();
return (_operator == Token::Not) ? shared_from_this() : TypePointer();
}
-TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer BoolType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (category() != _other->category())
return TypePointer();
- if (Token::isCompareOp(_operator) || _operator == Token::And || _operator == Token::Or)
+ if (_operator == Token::Equal || _operator == Token::NotEqual || _operator == Token::And || _operator == Token::Or)
return _other;
else
return TypePointer();
@@ -1368,25 +1505,24 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
{
if (*this == _convertTo)
return true;
- if (_convertTo.category() == Category::Integer)
- return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
if (_convertTo.category() == Category::Contract)
{
auto const& bases = contractDefinition().annotation().linearizedBaseContracts;
if (m_super && bases.size() <= 1)
return false;
- return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
- &dynamic_cast<ContractType const&>(_convertTo).contractDefinition()) != bases.end();
+ return find(
+ m_super ? ++bases.begin() : bases.begin(), bases.end(),
+ &dynamic_cast<ContractType const&>(_convertTo).contractDefinition()
+ ) != bases.end();
}
return false;
}
bool ContractType::isExplicitlyConvertibleTo(Type const& _convertTo) const
{
- return
- isImplicitlyConvertibleTo(_convertTo) ||
- _convertTo.category() == Category::Integer ||
- _convertTo.category() == Category::Contract;
+ if (auto const* addressType = dynamic_cast<AddressType const*>(&_convertTo))
+ return isPayable() || (addressType->stateMutability() < StateMutability::Payable);
+ return isImplicitlyConvertibleTo(_convertTo);
}
bool ContractType::isPayable() const
@@ -1395,14 +1531,14 @@ bool ContractType::isPayable() const
return fallbackFunction && fallbackFunction->isPayable();
}
-TypePointer ContractType::unaryOperatorResult(Token::Value _operator) const
+TypePointer ContractType::unaryOperatorResult(Token _operator) const
{
if (isSuper())
return TypePointer{};
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
-TypePointer ReferenceType::unaryOperatorResult(Token::Value _operator) const
+TypePointer ReferenceType::unaryOperatorResult(Token _operator) const
{
if (_operator != Token::Delete)
return TypePointer();
@@ -1416,8 +1552,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();
}
@@ -1452,12 +1586,18 @@ string ReferenceType::stringForReferencePart() const
string ReferenceType::identifierLocationSuffix() const
{
string id;
- if (location() == DataLocation::Storage)
+ switch (location())
+ {
+ case DataLocation::Storage:
id += "_storage";
- else if (location() == DataLocation::Memory)
+ break;
+ case DataLocation::Memory:
id += "_memory";
- else
+ break;
+ case DataLocation::CallData:
id += "_calldata";
+ break;
+ }
if (isPointer())
id += "_ptr";
return id;
@@ -1558,6 +1698,9 @@ bool ArrayType::operator==(Type const& _other) const
bool ArrayType::validForCalldata() const
{
+ if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType().get()))
+ if (!arrayBaseType->validForCalldata())
+ return false;
return unlimitedCalldataEncodedSize(true) <= numeric_limits<unsigned>::max();
}
@@ -1672,6 +1815,7 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
{
members.push_back({"length", make_shared<IntegerType>(256)});
if (isDynamicallySized() && location() == DataLocation::Storage)
+ {
members.push_back({"push", make_shared<FunctionType>(
TypePointers{baseType()},
TypePointers{make_shared<IntegerType>(256)},
@@ -1679,6 +1823,14 @@ MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
strings{string()},
isByteArray() ? FunctionType::Kind::ByteArrayPush : FunctionType::Kind::ArrayPush
)});
+ members.push_back({"pop", make_shared<FunctionType>(
+ TypePointers{},
+ TypePointers{},
+ strings{string()},
+ strings{string()},
+ FunctionType::Kind::ArrayPop
+ )});
+ }
}
return members;
}
@@ -1752,7 +1904,7 @@ TypePointer ArrayType::copyForLocation(DataLocation _location, bool _isPointer)
string ContractType::richIdentifier() const
{
- return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + std::to_string(m_contract.id());
+ return (m_super ? "t_super" : "t_contract") + parenthesizeUserIdentifier(m_contract.name()) + to_string(m_contract.id());
}
bool ContractType::operator==(Type const& _other) const
@@ -1799,7 +1951,7 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
continue;
auto memberType = dynamic_cast<FunctionType const*>(member.type.get());
solAssert(!!memberType, "Override changes type.");
- if (!memberType->hasEqualArgumentTypes(*functionType))
+ if (!memberType->hasEqualParameterTypes(*functionType))
continue;
functionWithEqualArgumentsFound = true;
break;
@@ -1821,47 +1973,9 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
&it.second->declaration()
));
}
- // In 0.5.0 address members are not populated into the contract.
- if (!_contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
- addNonConflictingAddressMembers(members);
return members;
}
-void ContractType::addNonConflictingAddressMembers(MemberList::MemberMap& _members)
-{
- MemberList::MemberMap addressMembers = IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr);
- for (auto const& addressMember: addressMembers)
- {
- bool clash = false;
- for (auto const& member: _members)
- {
- if (
- member.name == addressMember.name &&
- (
- // Members with different types are not allowed
- member.type->category() != addressMember.type->category() ||
- // Members must overload functions without clash
- (
- member.type->category() == Type::Category::Function &&
- dynamic_cast<FunctionType const&>(*member.type).hasEqualArgumentTypes(dynamic_cast<FunctionType const&>(*addressMember.type))
- )
- )
- )
- {
- clash = true;
- break;
- }
- }
-
- if (!clash)
- _members.push_back(MemberList::Member(
- addressMember.name,
- addressMember.type,
- addressMember.declaration
- ));
- }
-}
-
shared_ptr<FunctionType const> const& ContractType::newExpressionType() const
{
if (!m_constructorType)
@@ -1904,7 +2018,7 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
string StructType::richIdentifier() const
{
- return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + std::to_string(m_struct.id()) + identifierLocationSuffix();
+ return "t_struct" + parenthesizeUserIdentifier(m_struct.name()) + to_string(m_struct.id()) + identifierLocationSuffix();
}
bool StructType::operator==(Type const& _other) const
@@ -2009,8 +2123,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;
}
@@ -2106,7 +2223,7 @@ bool StructType::recursive() const
{
if (!m_recursive.is_initialized())
{
- auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector)
+ auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector, size_t /*_depth*/)
{
for (ASTPointer<VariableDeclaration> const& variable: _struct.members())
{
@@ -2123,14 +2240,14 @@ bool StructType::recursive() const
return *m_recursive;
}
-TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
+TypePointer EnumType::unaryOperatorResult(Token _operator) const
{
return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer();
}
string EnumType::richIdentifier() const
{
- return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + std::to_string(m_enum.id());
+ return "t_enum" + parenthesizeUserIdentifier(m_enum.name()) + to_string(m_enum.id());
}
bool EnumType::operator==(Type const& _other) const
@@ -2189,25 +2306,13 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
TypePointers const& targets = tupleType->components();
if (targets.empty())
return components().empty();
- if (components().size() != targets.size() && !targets.front() && !targets.back())
- return false; // (,a,) = (1,2,3,4) - unable to position `a` in the tuple.
- size_t minNumValues = targets.size();
- if (!targets.back() || !targets.front())
- --minNumValues; // wildcards can also match 0 components
- if (components().size() < minNumValues)
+ if (components().size() != targets.size())
return false;
- if (components().size() > targets.size() && targets.front() && targets.back())
- return false; // larger source and no wildcard
- bool fillRight = !targets.back() || targets.front();
- for (size_t i = 0; i < min(targets.size(), components().size()); ++i)
- {
- auto const& s = components()[fillRight ? i : components().size() - i - 1];
- auto const& t = targets[fillRight ? i : targets.size() - i - 1];
- if (!s && t)
+ for (size_t i = 0; i < targets.size(); ++i)
+ if (!components()[i] && targets[i])
return false;
- else if (s && t && !s->isImplicitlyConvertibleTo(*t))
+ else if (components()[i] && targets[i] && !components()[i]->isImplicitlyConvertibleTo(*targets[i]))
return false;
- }
return true;
}
else
@@ -2273,16 +2378,14 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
{
solAssert(!!_targetType, "");
TypePointers const& targetComponents = dynamic_cast<TupleType const&>(*_targetType).components();
- bool fillRight = !targetComponents.empty() && (!targetComponents.back() || targetComponents.front());
+ solAssert(components().size() == targetComponents.size(), "");
TypePointers tempComponents(targetComponents.size());
- for (size_t i = 0; i < min(targetComponents.size(), components().size()); ++i)
+ for (size_t i = 0; i < targetComponents.size(); ++i)
{
- size_t si = fillRight ? i : components().size() - i - 1;
- size_t ti = fillRight ? i : targetComponents.size() - i - 1;
- if (components()[si] && targetComponents[ti])
+ if (components()[i] && targetComponents[i])
{
- tempComponents[ti] = components()[si]->closestTemporaryType(targetComponents[ti]);
- solAssert(tempComponents[ti], "");
+ tempComponents[i] = components()[i]->closestTemporaryType(targetComponents[i]);
+ solAssert(tempComponents[i], "");
}
}
return make_shared<TupleType>(tempComponents);
@@ -2326,7 +2429,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("");
@@ -2446,7 +2549,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>();
@@ -2468,15 +2578,15 @@ 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;
case Kind::BareDelegateCall: id += "baredelegatecall"; break;
+ case Kind::BareStaticCall: id += "barestaticcall"; break;
case Kind::Creation: id += "creation"; break;
case Kind::Send: id += "send"; break;
case Kind::Transfer: id += "transfer"; break;
- case Kind::SHA3: id += "sha3"; break;
+ case Kind::KECCAK256: id += "keccak256"; break;
case Kind::Selfdestruct: id += "selfdestruct"; break;
case Kind::Revert: id += "revert"; break;
case Kind::ECRecover: id += "ecrecover"; break;
@@ -2495,6 +2605,7 @@ string FunctionType::richIdentifier() const
case Kind::AddMod: id += "addmod"; break;
case Kind::MulMod: id += "mulmod"; break;
case Kind::ArrayPush: id += "arraypush"; break;
+ case Kind::ArrayPop: id += "arraypop"; break;
case Kind::ByteArrayPush: id += "bytearraypush"; break;
case Kind::ObjectCreation: id += "objectcreation"; break;
case Kind::Assert: id += "assert"; break;
@@ -2503,7 +2614,7 @@ string FunctionType::richIdentifier() const
case Kind::ABIEncodePacked: id += "abiencodepacked"; break;
case Kind::ABIEncodeWithSelector: id += "abiencodewithselector"; break;
case Kind::ABIEncodeWithSignature: id += "abiencodewithsignature"; break;
- default: solAssert(false, "Unknown function location."); break;
+ case Kind::ABIDecode: id += "abidecode"; break;
}
id += "_" + stateMutabilityToString(m_stateMutability);
id += identifierList(m_parameterTypes) + "returns" + identifierList(m_returnParameterTypes);
@@ -2520,51 +2631,54 @@ bool FunctionType::operator==(Type const& _other) const
{
if (_other.category() != category())
return false;
-
FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
- if (
- m_kind != other.m_kind ||
- m_stateMutability != other.stateMutability() ||
- m_parameterTypes.size() != other.m_parameterTypes.size() ||
- m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
- )
+ if (!equalExcludingStateMutability(other))
return false;
-
- auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
- if (
- !equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(), other.m_parameterTypes.cbegin(), typeCompare) ||
- !equal(m_returnParameterTypes.cbegin(), m_returnParameterTypes.cend(), other.m_returnParameterTypes.cbegin(), typeCompare)
- )
- return false;
- //@todo this is ugly, but cannot be prevented right now
- if (m_gasSet != other.m_gasSet || m_valueSet != other.m_valueSet)
- return false;
- if (bound() != other.bound())
- return false;
- if (bound() && *selfType() != *other.selfType())
+ if (m_stateMutability != other.stateMutability())
return false;
return true;
}
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())
- return true;
- }
+ if (m_kind == Kind::External && _convertTo == AddressType::address())
+ return true;
return _convertTo.category() == category();
}
-TypePointer FunctionType::unaryOperatorResult(Token::Value _operator) const
+bool FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const
+{
+ if (_convertTo.category() != category())
+ return false;
+
+ FunctionType const& convertTo = dynamic_cast<FunctionType const&>(_convertTo);
+
+ if (!equalExcludingStateMutability(convertTo))
+ return false;
+
+ // non-payable should not be convertible to payable
+ if (m_stateMutability != StateMutability::Payable && convertTo.stateMutability() == StateMutability::Payable)
+ return false;
+
+ // payable should be convertible to non-payable, because you are free to pay 0 ether
+ if (m_stateMutability == StateMutability::Payable && convertTo.stateMutability() == StateMutability::NonPayable)
+ return true;
+
+ // e.g. pure should be convertible to view, but not the other way around.
+ if (m_stateMutability > convertTo.stateMutability())
+ return false;
+
+ return true;
+}
+
+TypePointer FunctionType::unaryOperatorResult(Token _operator) const
{
- if (_operator == Token::Value::Delete)
+ if (_operator == Token::Delete)
return make_shared<TupleType>();
return TypePointer();
}
-TypePointer FunctionType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+TypePointer FunctionType::binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
if (_other->category() != category() || !(_operator == Token::Equal || _operator == Token::NotEqual))
return TypePointer();
@@ -2640,15 +2754,16 @@ unsigned FunctionType::sizeOnStack() const
switch(kind)
{
case Kind::External:
- case Kind::CallCode:
case Kind::DelegateCall:
size = 2;
break;
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
case Kind::Internal:
case Kind::ArrayPush:
+ case Kind::ArrayPop:
case Kind::ByteArrayPush:
size = 1;
break;
@@ -2713,6 +2828,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
{
MemberList::MemberMap members;
if (m_kind == Kind::External)
@@ -2801,7 +2917,7 @@ bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePoin
);
}
-bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const
+bool FunctionType::hasEqualParameterTypes(FunctionType const& _other) const
{
if (m_parameterTypes.size() != _other.m_parameterTypes.size())
return false;
@@ -2813,6 +2929,38 @@ bool FunctionType::hasEqualArgumentTypes(FunctionType const& _other) const
);
}
+bool FunctionType::hasEqualReturnTypes(FunctionType const& _other) const
+{
+ if (m_returnParameterTypes.size() != _other.m_returnParameterTypes.size())
+ return false;
+ return equal(
+ m_returnParameterTypes.cbegin(),
+ m_returnParameterTypes.cend(),
+ _other.m_returnParameterTypes.cbegin(),
+ [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; }
+ );
+}
+
+bool FunctionType::equalExcludingStateMutability(FunctionType const& _other) const
+{
+ if (m_kind != _other.m_kind)
+ return false;
+
+ if (!hasEqualParameterTypes(_other) || !hasEqualReturnTypes(_other))
+ return false;
+
+ //@todo this is ugly, but cannot be prevented right now
+ if (m_gasSet != _other.m_gasSet || m_valueSet != _other.m_valueSet)
+ return false;
+
+ if (bound() != _other.bound())
+ return false;
+
+ solAssert(!bound() || *selfType() == *_other.selfType(), "");
+
+ return true;
+}
+
bool FunctionType::isBareCall() const
{
switch (m_kind)
@@ -2820,6 +2968,7 @@ bool FunctionType::isBareCall() const
case Kind::BareCall:
case Kind::BareCallCode:
case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
case Kind::ECRecover:
case Kind::SHA256:
case Kind::RIPEMD160:
@@ -2833,6 +2982,16 @@ string FunctionType::externalSignature() const
{
solAssert(m_declaration != nullptr, "External signature of function needs declaration");
solAssert(!m_declaration->name().empty(), "Fallback function has no signature.");
+ switch (kind())
+ {
+ case Kind::Internal:
+ case Kind::External:
+ case Kind::DelegateCall:
+ case Kind::Event:
+ break;
+ default:
+ solAssert(false, "Invalid function type for requesting external signature.");
+ }
bool const inLibrary = dynamic_cast<ContractDefinition const&>(*m_declaration->scope()).isLibrary();
FunctionTypePointer external = interfaceFunctionType();
@@ -2859,7 +3018,7 @@ bool FunctionType::isPure() const
// FIXME: replace this with m_stateMutability == StateMutability::Pure once
// the callgraph analyzer is in place
return
- m_kind == Kind::SHA3 ||
+ m_kind == Kind::KECCAK256 ||
m_kind == Kind::ECRecover ||
m_kind == Kind::SHA256 ||
m_kind == Kind::RIPEMD160 ||
@@ -2869,7 +3028,8 @@ bool FunctionType::isPure() const
m_kind == Kind::ABIEncode ||
m_kind == Kind::ABIEncodePacked ||
m_kind == Kind::ABIEncodeWithSelector ||
- m_kind == Kind::ABIEncodeWithSignature;
+ m_kind == Kind::ABIEncodeWithSignature ||
+ m_kind == Kind::ABIDecode;
}
TypePointers FunctionType::parseElementaryTypeVector(strings const& _types)
@@ -2954,6 +3114,26 @@ ASTPointer<ASTString> FunctionType::documentation() const
return ASTPointer<ASTString>();
}
+bool FunctionType::padArguments() const
+{
+ // No padding only for hash functions, low-level calls and the packed encoding function.
+ switch (m_kind)
+ {
+ case Kind::BareCall:
+ case Kind::BareCallCode:
+ case Kind::BareDelegateCall:
+ case Kind::BareStaticCall:
+ case Kind::SHA256:
+ case Kind::RIPEMD160:
+ case Kind::KECCAK256:
+ case Kind::ABIEncodePacked:
+ return false;
+ default:
+ return true;
+ }
+ return true;
+}
+
string MappingType::richIdentifier() const
{
return "t_mapping" + identifierList(m_keyType, m_valueType);
@@ -3093,7 +3273,7 @@ string ModifierType::toString(bool _short) const
string ModuleType::richIdentifier() const
{
- return "t_module_" + std::to_string(m_sourceUnit.id());
+ return "t_module_" + to_string(m_sourceUnit.id());
}
bool ModuleType::operator==(Type const& _other) const
@@ -3129,8 +3309,6 @@ string MagicType::richIdentifier() const
return "t_magic_transaction";
case Kind::ABI:
return "t_magic_abi";
- default:
- solAssert(false, "Unknown kind of magic");
}
return "";
}
@@ -3149,7 +3327,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>(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)},
@@ -3158,7 +3336,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>(StateMutability::Payable)},
{"gas", make_shared<IntegerType>(256)},
{"value", make_shared<IntegerType>(256)},
{"data", make_shared<ArrayType>(DataLocation::CallData)},
@@ -3166,7 +3344,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>(StateMutability::Payable)},
{"gasprice", make_shared<IntegerType>(256)}
});
case Kind::ABI:
@@ -3206,6 +3384,15 @@ MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
FunctionType::Kind::ABIEncodeWithSignature,
true,
StateMutability::Pure
+ )},
+ {"decode", make_shared<FunctionType>(
+ TypePointers(),
+ TypePointers(),
+ strings{},
+ strings{},
+ FunctionType::Kind::ABIDecode,
+ true,
+ StateMutability::Pure
)}
});
default:
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 95821634..24ace447 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -51,6 +51,15 @@ using FunctionTypePointer = std::shared_ptr<FunctionType const>;
using TypePointers = std::vector<TypePointer>;
using rational = boost::rational<dev::bigint>;
+inline rational makeRational(bigint const& _numerator, bigint const& _denominator)
+{
+ solAssert(_denominator != 0, "division by zero");
+ // due to a bug in certain versions of boost the denominator has to be positive
+ if (_denominator < 0)
+ return rational(-_numerator, -_denominator);
+ else
+ return rational(_numerator, _denominator);
+}
enum class DataLocation { Storage, CallData, Memory };
@@ -95,9 +104,7 @@ public:
using MemberMap = std::vector<Member>;
- MemberList() {}
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {}
- MemberList& operator=(MemberList&& _other);
void combine(MemberList const& _other);
TypePointer memberType(std::string const& _name) const
{
@@ -132,6 +139,8 @@ private:
mutable std::unique_ptr<StorageOffsets> m_storageOffsets;
};
+static_assert(std::is_nothrow_move_constructible<MemberList>::value, "MemberList should be noexcept move constructible");
+
/**
* Abstract base class that forms the root of the type hierarchy.
*/
@@ -141,7 +150,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
@@ -151,7 +160,8 @@ public:
/// @name Factory functions
/// Factory functions that convert an AST @ref TypeName to a Type.
static TypePointer fromElementaryTypeName(ElementaryTypeNameToken const& _type);
- /// Converts a given elementary type name with optional suffix " memory" to a type pointer.
+ /// Converts a given elementary type name with optional data location
+ /// suffix " storage", " calldata" or " memory" to a type pointer. If suffix not given, defaults to " storage".
static TypePointer fromElementaryTypeName(std::string const& _name);
/// @}
@@ -171,9 +181,9 @@ public:
/// only if they have the same identifier.
/// The identifier should start with "t_".
/// Will not contain any character which would be invalid as an identifier.
- std::string identifier() const { return escapeIdentifier(richIdentifier()); }
+ 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)
@@ -187,13 +197,13 @@ public:
/// @returns the resulting type of applying the given unary operator or an empty pointer if
/// this is not possible.
/// The default implementation does not allow any unary operator.
- virtual TypePointer unaryOperatorResult(Token::Value) const { return TypePointer(); }
+ virtual TypePointer unaryOperatorResult(Token) const { return TypePointer(); }
/// @returns the resulting type of applying the given binary operator or an empty pointer if
/// this is not possible.
/// The default implementation allows comparison operators if a common type exists
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const
{
- return Token::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
+ return TokenTraits::isCompareOp(_operator) ? commonType(shared_from_this(), _other) : TypePointer();
}
virtual bool operator==(Type const& _other) const { return category() == _other.category(); }
@@ -279,6 +289,11 @@ public:
/// This for example returns address for contract types.
/// If there is no such type, returns an empty shared pointer.
virtual TypePointer encodingType() const { return TypePointer(); }
+ /// @returns the encoding type used under the given circumstances for the type of an expression
+ /// when used for e.g. abi.encode(...) or the empty pointer if the object
+ /// cannot be encoded.
+ /// This is different from encodingType since it takes implicit conversions into account.
+ TypePointer fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool _packed) const;
/// @returns a (simpler) type that is used when decoding this type in calldata.
virtual TypePointer decodingType() const { return encodingType(); }
/// @returns a type that will be used outside of Solidity for e.g. function signatures.
@@ -308,15 +323,57 @@ protected:
};
/**
- * Any kind of integer type (signed, unsigned, address).
+ * Type for addresses.
+ */
+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(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 _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _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; }
+
+ 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;
+};
+
+/**
+ * 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; }
explicit IntegerType(unsigned _bits, Modifier _modifier = Modifier::Unsigned);
@@ -324,8 +381,8 @@ public:
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) 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 TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@@ -333,17 +390,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;
@@ -371,8 +423,8 @@ public:
virtual std::string richIdentifier() const override;
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) 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 TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual bool operator==(Type const& _other) const override;
@@ -417,16 +469,16 @@ public:
virtual Category category() const override { return Category::RationalNumber; }
- /// @returns true if the literal is a valid integer.
- static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+ static TypePointer forLiteral(Literal const& _literal);
- explicit RationalNumberType(rational const& _value):
- m_value(_value)
+ explicit RationalNumberType(rational const& _value, TypePointer const& _compatibleBytesType = TypePointer()):
+ m_value(_value), m_compatibleBytesType(_compatibleBytesType)
{}
+
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) 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 TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
@@ -440,7 +492,8 @@ public:
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
std::shared_ptr<IntegerType const> integerType() const;
- /// @returns the smallest fixed type that can hold the value or incurs the least precision loss.
+ /// @returns the smallest fixed type that can hold the value or incurs the least precision loss,
+ /// unless the value was truncated, then a suitable type will be chosen to indicate such event.
/// If the integer part does not fit, returns an empty pointer.
std::shared_ptr<FixedPointType const> fixedPointType() const;
@@ -456,6 +509,13 @@ public:
private:
rational m_value;
+ /// Bytes type to which the rational can be explicitly converted.
+ /// Empty for all rationals that are not directly parsed from hex literals.
+ TypePointer m_compatibleBytesType;
+
+ /// @returns true if the literal is a valid integer.
+ static std::tuple<bool, rational> isValidLiteral(Literal const& _literal);
+
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
@@ -475,7 +535,7 @@ public:
explicit StringLiteralType(Literal const& _literal);
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -512,8 +572,8 @@ public:
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override { return _padded && m_bytes > 0 ? 32 : m_bytes; }
virtual unsigned storageBytes() const override { return m_bytes; }
@@ -539,8 +599,8 @@ public:
BoolType() {}
virtual Category category() const override { return Category::Bool; }
virtual std::string richIdentifier() const override { return "t_bool"; }
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token _operator, TypePointer const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override{ return _padded ? 32 : 1; }
virtual unsigned storageBytes() const override { return 1; }
@@ -562,8 +622,8 @@ public:
explicit ReferenceType(DataLocation _location): m_location(_location) {}
DataLocation location() const { return m_location; }
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -614,6 +674,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.
@@ -691,11 +754,11 @@ public:
virtual Category category() const override { return Category::Contract; }
explicit ContractType(ContractDefinition const& _contract, bool _super = false):
m_contract(_contract), m_super(_super) {}
- /// Contracts can be implicitly converted to super classes and to addresses.
+ /// Contracts can be implicitly converted only to base contracts.
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
- /// Contracts can be converted to themselves and to integers.
+ /// Contracts can only be explicitly converted to address types and base contracts.
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded ) const override
@@ -715,7 +778,7 @@ public:
{
if (isSuper())
return TypePointer{};
- return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address);
+ return std::make_shared<AddressType>(isPayable() ? StateMutability::Payable : StateMutability::NonPayable);
}
virtual TypePointer interfaceType(bool _inLibrary) const override
{
@@ -739,8 +802,6 @@ public:
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
private:
- static void addNonConflictingAddressMembers(MemberList::MemberMap& _members);
-
ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members.
@@ -813,7 +874,7 @@ class EnumType: public Type
public:
virtual Category category() const override { return Category::Enum; }
explicit EnumType(EnumDefinition const& _enum): m_enum(_enum) {}
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override
@@ -857,7 +918,7 @@ public:
virtual bool isImplicitlyConvertibleTo(Type const& _other) const override;
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual std::string toString(bool) const override;
virtual bool canBeStored() const override { return false; }
virtual u256 storageSize() const override;
@@ -887,15 +948,15 @@ 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
BareDelegateCall, ///< DELEGATECALL without function hash
+ BareStaticCall, ///< STATICCALL without function hash
Creation, ///< external call using CREATE
Send, ///< CALL, but without data and gas
Transfer, ///< CALL, but without data and throws on error
- SHA3, ///< SHA3
+ KECCAK256, ///< KECCAK256
Selfdestruct, ///< SELFDESTRUCT
Revert, ///< REVERT
ECRecover, ///< CALL to special contract for ecrecover
@@ -913,6 +974,7 @@ public:
AddMod, ///< ADDMOD
MulMod, ///< MULMOD
ArrayPush, ///< .push() to a dynamically sized array in storage
+ ArrayPop, ///< .pop() from a dynamically sized array in storage
ByteArrayPush, ///< .push() to a dynamically sized byte array in storage
ObjectCreation, ///< array creation using new
Assert, ///< assert()
@@ -921,7 +983,8 @@ public:
ABIEncodePacked,
ABIEncodeWithSelector,
ABIEncodeWithSignature,
- GasLeft ///< gasleft()
+ ABIDecode,
+ GasLeft, ///< gasleft()
};
virtual Category category() const override { return Category::Function; }
@@ -1000,9 +1063,10 @@ public:
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
+ virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override;
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override;
- virtual TypePointer unaryOperatorResult(Token::Value _operator) const override;
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override;
+ virtual TypePointer unaryOperatorResult(Token _operator) const override;
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override;
virtual std::string canonicalName() const override;
virtual std::string toString(bool _short) const override;
virtual unsigned calldataEncodedSize(bool _padded) const override;
@@ -1029,10 +1093,14 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
- /// @returns true if the types of parameters are equal (does't check return parameter types)
- bool hasEqualArgumentTypes(FunctionType const& _other) const;
-
- /// @returns true if the ABI is used for this call (only meaningful for external calls)
+ /// @returns true if the types of parameters are equal (does not check return parameter types)
+ bool hasEqualParameterTypes(FunctionType const& _other) const;
+ /// @returns true iff the return types are equal (does not check parameter types)
+ bool hasEqualReturnTypes(FunctionType const& _other) const;
+ /// @returns true iff the function type is equal to the given type, ignoring state mutability differences.
+ bool equalExcludingStateMutability(FunctionType const& _other) const;
+
+ /// @returns true if the ABI is NOT used for this call (only meaningful for external calls)
bool isBareCall() const;
Kind const& kind() const { return m_kind; }
StateMutability stateMutability() const { return m_stateMutability; }
@@ -1056,18 +1124,22 @@ public:
ASTPointer<ASTString> documentation() const;
/// true iff arguments are to be padded to multiples of 32 bytes for external calls
- bool padArguments() const { return !(m_kind == Kind::SHA3 || m_kind == Kind::SHA256 || m_kind == Kind::RIPEMD160 || m_kind == Kind::ABIEncodePacked); }
+ /// The only functions that do not pad are hash functions, the low-level call functions
+ /// and abi.encodePacked.
+ bool padArguments() const;
bool takesArbitraryParameters() const { return m_arbitraryParameters; }
/// true iff the function takes a single bytes parameter and it is passed on without padding.
- /// @todo until 0.5.0, this is just a "recommendation".
bool takesSinglePackedBytesParameter() const
{
- // @todo add the call kinds here with 0.5.0 and perhaps also log0.
switch (m_kind)
{
- case FunctionType::Kind::SHA3:
+ case FunctionType::Kind::KECCAK256:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:
+ case FunctionType::Kind::BareCall:
+ case FunctionType::Kind::BareCallCode:
+ case FunctionType::Kind::BareDelegateCall:
+ case FunctionType::Kind::BareStaticCall:
return true;
default:
return false;
@@ -1124,7 +1196,7 @@ public:
virtual std::string toString(bool _short) const override;
virtual std::string canonicalName() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual TypePointer encodingType() const override
{
return std::make_shared<IntegerType>(256);
@@ -1157,7 +1229,7 @@ public:
explicit TypeType(TypePointer const& _actualType): m_actualType(_actualType) {}
TypePointer const& actualType() const { return m_actualType; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
@@ -1182,7 +1254,7 @@ public:
virtual Category category() const override { return Category::Modifier; }
explicit ModifierType(ModifierDefinition const& _modifier);
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual bool canBeStored() const override { return false; }
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
@@ -1208,7 +1280,7 @@ public:
explicit ModuleType(SourceUnit const& _source): m_sourceUnit(_source) {}
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
@@ -1235,7 +1307,7 @@ public:
explicit MagicType(Kind _kind): m_kind(_kind) {}
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override
{
return TypePointer();
}
@@ -1268,7 +1340,7 @@ public:
virtual std::string richIdentifier() const override { return "t_inaccessible"; }
virtual bool isImplicitlyConvertibleTo(Type const&) const override { return false; }
virtual bool isExplicitlyConvertibleTo(Type const&) const override { return false; }
- virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual TypePointer binaryOperatorResult(Token, TypePointer const&) const override { return TypePointer(); }
virtual unsigned calldataEncodedSize(bool _padded) const override { (void)_padded; return 32; }
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return false; }