diff options
Diffstat (limited to 'libsolidity/ast')
-rw-r--r-- | libsolidity/ast/AST.cpp | 157 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 128 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 52 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.h | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTPrinter.cpp | 8 | ||||
-rw-r--r-- | libsolidity/ast/ASTUtils.cpp | 48 | ||||
-rw-r--r-- | libsolidity/ast/ASTUtils.h | 54 | ||||
-rw-r--r-- | libsolidity/ast/ExperimentalFeatures.h | 5 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 769 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 194 |
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; } |