diff options
Diffstat (limited to 'libsolidity/ast')
-rw-r--r-- | libsolidity/ast/AST.cpp | 142 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 68 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 25 | ||||
-rw-r--r-- | libsolidity/ast/ExperimentalFeatures.h | 5 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 697 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 122 |
7 files changed, 692 insertions, 374 deletions
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 80f5d642..8e7a81a6 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -312,7 +312,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const case Declaration::Visibility::External: return {}; default: - solAssert(false, "visibility() should not return a Visibility"); + solAssert(false, "visibility() should return a Visibility"); } } else @@ -328,7 +328,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const case Declaration::Visibility::External: return make_shared<FunctionType>(*this, _internal); default: - solAssert(false, "visibility() should not return a Visibility"); + solAssert(false, "visibility() should return a Visibility"); } } @@ -347,15 +347,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 +397,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 +418,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 +426,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 +450,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 +467,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 { - return isLocalVariable() && !isCallableParameter(); + 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 +{ + 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 @@ -535,13 +604,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) @@ -601,7 +663,7 @@ bool Literal::passesAddressChecksum() const return dev::passesAddressChecksum(value(), true); } -std::string Literal::getChecksummedAddress() const +string Literal::getChecksummedAddress() const { solAssert(isHexNumber(), "Expected hex number"); /// Pad literal to be a proper hex address. diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index fa0d6921..f3464f92 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; } @@ -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; 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..8d52851a 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,9 +325,6 @@ 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("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("superFunction", idOrNull(_node.annotation().superFunction)), make_pair("visibility", Declaration::visibilityToString(_node.visibility())), @@ -397,10 +394,15 @@ bool ASTJsonConverter::visit(EventDefinition const& _node) bool ASTJsonConverter::visit(ElementaryTypeName const& _node) { - setJsonNode(_node, "ElementaryTypeName", { + std::vector<pair<string, Json::Value>> attributes = { make_pair("name", _node.typeName().toString()), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) - }); + }; + + if (_node.stateMutability()) + attributes.emplace_back(make_pair("stateMutability", stateMutabilityToString(*_node.stateMutability()))); + + setJsonNode(_node, "ElementaryTypeName", std::move(attributes)); return false; } @@ -418,11 +420,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)) @@ -555,8 +554,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())), @@ -745,12 +744,14 @@ 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"; + case VariableDeclaration::Location::CallData: + return "calldata"; default: solAssert(false, "Unknown declaration location."); } 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..25702f19 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -33,10 +33,13 @@ #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,6 +255,17 @@ 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()), @@ -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; + vector<string> nameParts; + boost::split(nameParts, _name, boost::is_any_of(" ")); + solAssert(nameParts.size() == 1 || nameParts.size() == 2, "Cannot parse elementary type: " + _name); Token::Value token; - tie(token, firstNum, secondNum) = Token::fromIdentifierOrKeyword(name); + unsigned short firstNum, secondNum; + tie(token, firstNum, secondNum) = Token::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,6 +450,98 @@ 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->value()); +} + +TypePointer AddressType::unaryOperatorResult(Token::Value _operator) const +{ + return _operator == Token::Delete ? make_shared<TupleType>() : TypePointer(); +} + + +TypePointer AddressType::binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const +{ + // Addresses can only be compared. + if (!Token::isCompareOp(_operator)) + return TypePointer(); + + return Type::commonType(shared_from_this(), _other); +} + +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 { @@ -416,7 +551,7 @@ bool isValidShiftAndAmountType(Token::Value _operator, Type const& _shiftAmountT if (_operator == Token::SHR) return false; else if (IntegerType const* otherInt = dynamic_cast<decltype(otherInt)>(&_shiftAmountType)) - return !otherInt->isAddress(); + return true; else if (RationalNumberType const* otherRat = dynamic_cast<decltype(otherRat)>(&_shiftAmountType)) return !otherRat->isFractional() && otherRat->integerType() && !otherRat->integerType()->isSigned(); else @@ -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,9 +598,10 @@ 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; } @@ -485,10 +610,7 @@ TypePointer IntegerType::unaryOperatorResult(Token::Value _operator) const // "delete" is ok for all integer types if (_operator == Token::Delete) return make_shared<TupleType>(); - // no further unary operators for addresses - else if (isAddress()) - return TypePointer(); - // for non-address integers, we allow +, -, ++ and -- + // we allow +, -, ++ and -- else if (_operator == Token::Add || _operator == Token::Sub || _operator == Token::Inc || _operator == Token::Dec || _operator == Token::BitNot) @@ -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()) @@ -548,8 +660,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe if (Token::isShiftOp(_operator)) { // Shifts are not symmetric with respect to the type - if (isAddress()) - return TypePointer(); if (isValidShiftAndAmountType(_operator, *_other)) return shared_from_this(); else @@ -567,9 +677,6 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return TypePointer(); if (auto intType = dynamic_pointer_cast<IntegerType const>(commonType)) { - // Nothing else can be done with addresses - if (intType->isAddress()) - return TypePointer(); // Signed EXP is not allowed if (Token::Exp == _operator && intType->isSigned()) return TypePointer(); @@ -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,8 +717,7 @@ bool FixedPointType::isImplicitlyConvertibleTo(Type const& _convertTo) const bool FixedPointType::isExplicitlyConvertibleTo(Type const& _convertTo) const { - return _convertTo.category() == category() || - (_convertTo.category() == Category::Integer && !dynamic_cast<IntegerType const&>(_convertTo).isAddress()); + return _convertTo.category() == category() || _convertTo.category() == Category::Integer; } TypePointer FixedPointType::unaryOperatorResult(Token::Value _operator) const @@ -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,7 +763,7 @@ 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); @@ -741,36 +832,66 @@ 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 digitCount = count_if( + _literal.value().begin() + 2, // skip "0x" + _literal.value().end(), + [](unsigned char _c) -> bool { return isxdigit(_c); } + ); + // require even number of digits + if (!(digitCount & 1)) + 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.value(); + boost::erase_all(valueString, "_");// Remove underscore separators - if (boost::starts_with(_literal.value(), "0x")) + auto expPoint = find(valueString.begin(), valueString.end(), 'e'); + if (expPoint == valueString.end()) + expPoint = find(valueString.begin(), valueString.end(), 'E'); + + 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 +915,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,49 +963,53 @@ 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 @@ -926,7 +1051,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other); if (Token::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 +1107,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,7 +1143,7 @@ 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); @@ -1035,7 +1159,6 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ } case Token::SHL: { - using boost::multiprecision::pow; if (fractional) return TypePointer(); else if (other.m_value < 0) @@ -1049,7 +1172,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 +1180,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 +1191,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 +1224,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,7 +1269,7 @@ u256 RationalNumberType::literalValue(Literal const*) const // its value. u256 value; - bigint shiftedValue; + bigint shiftedValue; if (!isFractional()) shiftedValue = m_value.numerator(); @@ -1137,7 +1278,7 @@ u256 RationalNumberType::literalValue(Literal const*) const auto fixed = fixedPointType(); solAssert(fixed, ""); 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 @@ -1180,7 +1321,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 +1330,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,7 +1423,8 @@ 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(); } @@ -1325,7 +1468,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 @@ -1358,7 +1501,7 @@ TypePointer BoolType::binaryOperatorResult(Token::Value _operator, TypePointer c { 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 +1511,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 @@ -1416,8 +1558,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 +1592,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; @@ -1672,6 +1818,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 +1826,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 +1907,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 +1954,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 +1976,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 +2021,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 @@ -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()) { @@ -2130,7 +2247,7 @@ TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const 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); @@ -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,43 +2631,46 @@ 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()) + if (m_kind == Kind::External && _convertTo.category() == Category::Address) return true; - } return _convertTo.category() == category(); } +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::Value _operator) const { if (_operator == Token::Value::Delete) @@ -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..a2d18b0a 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -95,9 +95,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 +130,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 +141,7 @@ public: virtual ~Type() = default; enum class Category { - Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, + Address, Integer, RationalNumber, StringLiteral, Bool, FixedPoint, Array, FixedBytes, Contract, Struct, Function, Enum, Tuple, Mapping, TypeType, Modifier, Magic, Module, InaccessibleDynamic @@ -151,7 +151,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,7 +172,7 @@ 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 /// "opening parenthesis", _$ as "closing parenthesis", _$_ as "comma" and any $ that @@ -279,6 +280,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,14 +314,52 @@ protected: }; /** - * Any kind of integer type (signed, unsigned, address). + * Type for addresses. + */ +class AddressType: public Type +{ +public: + virtual Category category() const override { return Category::Address; } + + explicit AddressType(StateMutability _stateMutability); + + virtual std::string richIdentifier() const override; + virtual bool isImplicitlyConvertibleTo(Type const& _other) const override; + virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual TypePointer unaryOperatorResult(Token::Value _operator) const override; + virtual TypePointer binaryOperatorResult(Token::Value _operator, TypePointer const& _other) const override; + + virtual bool operator==(Type const& _other) const override; + + virtual unsigned calldataEncodedSize(bool _padded = true) const override { return _padded ? 32 : 160 / 8; } + virtual unsigned storageBytes() const override { return 160 / 8; } + virtual bool isValueType() const override { return true; } + + 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; } @@ -333,17 +377,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; @@ -417,12 +456,12 @@ 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; @@ -440,7 +479,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 +496,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); @@ -691,9 +738,9 @@ 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 std::string richIdentifier() const override; @@ -715,7 +762,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 +786,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. @@ -887,15 +932,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 +958,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 +967,8 @@ public: ABIEncodePacked, ABIEncodeWithSelector, ABIEncodeWithSignature, - GasLeft ///< gasleft() + ABIDecode, + GasLeft, ///< gasleft() }; virtual Category category() const override { return Category::Function; } @@ -1000,6 +1047,7 @@ 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; @@ -1029,10 +1077,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 +1108,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; |