diff options
author | chriseth <c@ethdev.com> | 2015-11-30 23:06:44 +0800 |
---|---|---|
committer | chriseth <c@ethdev.com> | 2015-11-30 23:06:44 +0800 |
commit | e9c7837c154482a72c8519fbdc9376693ce9a1d5 (patch) | |
tree | 1985b57b4e221fe4e4b5cc14a5049b8c0c46ce6c | |
parent | a8736b7b271dac117f15164cf4d2dfabcdd2c6fd (diff) | |
parent | f9e52c9db1ef23000f5721a462aba3fa8d681749 (diff) | |
download | dexon-solidity-e9c7837c154482a72c8519fbdc9376693ce9a1d5.tar.gz dexon-solidity-e9c7837c154482a72c8519fbdc9376693ce9a1d5.tar.zst dexon-solidity-e9c7837c154482a72c8519fbdc9376693ce9a1d5.zip |
Merge pull request #251 from chriseth/bind2
Bind library functions to types.
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 4 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 140 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.h | 8 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 22 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/AST.h | 28 | ||||
-rw-r--r-- | libsolidity/ast/ASTForward.h | 1 | ||||
-rw-r--r-- | libsolidity/ast/ASTVisitor.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/AST_accept.h | 22 | ||||
-rw-r--r-- | libsolidity/ast/Types.cpp | 395 | ||||
-rw-r--r-- | libsolidity/ast/Types.h | 74 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 20 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.h | 1 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 160 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 15 |
16 files changed, 605 insertions, 292 deletions
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 0d9b2477..1547a274 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -160,8 +160,8 @@ private: void fatalDeclarationError(SourceLocation _sourceLocation, std::string const& _description); std::map<ASTNode const*, DeclarationContainer>& m_scopes; - Declaration const* m_currentScope; - VariableScope* m_currentFunction; + Declaration const* m_currentScope = nullptr; + VariableScope* m_currentFunction = nullptr; ErrorList& m_errors; }; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index e5b1c52b..17f93029 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -31,28 +31,6 @@ using namespace dev; using namespace dev::solidity; -bool ReferencesResolver::visit(Return const& _return) -{ - _return.annotation().functionReturnParameters = m_returnParameters; - return true; -} - -void ReferencesResolver::endVisit(NewExpression const& _new) -{ - typeFor(_new.typeName()); -} - -bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName) -{ - Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); - if (!declaration) - fatalDeclarationError(_typeName.location(), "Identifier not found or not unique."); - - _typeName.annotation().referencedDeclaration = declaration; - - return true; -} - bool ReferencesResolver::resolve(ASTNode const& _root) { try @@ -79,6 +57,67 @@ bool ReferencesResolver::visit(Identifier const& _identifier) return false; } +bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) +{ + _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + return true; +} + +void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) +{ + Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath()); + if (!declaration) + fatalDeclarationError(_typeName.location(), "Identifier not found or not unique."); + + _typeName.annotation().referencedDeclaration = declaration; + + if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) + _typeName.annotation().type = make_shared<StructType>(*structDef); + else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration)) + _typeName.annotation().type = make_shared<EnumType>(*enumDef); + else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) + _typeName.annotation().type = make_shared<ContractType>(*contract); + else + fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); +} + +void ReferencesResolver::endVisit(Mapping const& _typeName) +{ + TypePointer keyType = _typeName.keyType().annotation().type; + TypePointer valueType = _typeName.valueType().annotation().type; + // Convert key type to memory. + keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType); + // Convert value type to storage reference. + valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType); + _typeName.annotation().type = make_shared<MappingType>(keyType, valueType); +} + +void ReferencesResolver::endVisit(ArrayTypeName const& _typeName) +{ + TypePointer baseType = _typeName.baseType().annotation().type; + if (baseType->storageBytes() == 0) + fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array."); + if (Expression const* length = _typeName.length()) + { + if (!length->annotation().type) + ConstantEvaluator e(*length); + + auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get()); + if (!lengthType) + fatalTypeError(length->location(), "Invalid array length."); + else + _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); + } + else + _typeName.annotation().type = make_shared<ArrayType>(DataLocation::Storage, baseType); +} + +bool ReferencesResolver::visit(Return const& _return) +{ + _return.annotation().functionReturnParameters = m_returnParameters; + return true; +} + void ReferencesResolver::endVisit(VariableDeclaration const& _variable) { if (_variable.annotation().type) @@ -87,7 +126,7 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) TypePointer type; if (_variable.typeName()) { - type = typeFor(*_variable.typeName()); + type = _variable.typeName()->annotation().type; using Location = VariableDeclaration::Location; Location loc = _variable.referenceLocation(); // References are forced to calldata for external function parameters (not return) @@ -167,61 +206,6 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) _variable.annotation().type = type; } -TypePointer ReferencesResolver::typeFor(TypeName const& _typeName) -{ - if (_typeName.annotation().type) - return _typeName.annotation().type; - - TypePointer type; - if (auto elemTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName)) - type = Type::fromElementaryTypeName(elemTypeName->typeName()); - else if (auto typeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName)) - { - Declaration const* declaration = typeName->annotation().referencedDeclaration; - solAssert(!!declaration, ""); - - if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration)) - type = make_shared<StructType>(*structDef); - else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration)) - type = make_shared<EnumType>(*enumDef); - else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) - type = make_shared<ContractType>(*contract); - else - fatalTypeError(typeName->location(), "Name has to refer to a struct, enum or contract."); - } - else if (auto mapping = dynamic_cast<Mapping const*>(&_typeName)) - { - TypePointer keyType = typeFor(mapping->keyType()); - TypePointer valueType = typeFor(mapping->valueType()); - // Convert key type to memory. - keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType); - // Convert value type to storage reference. - valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType); - type = make_shared<MappingType>(keyType, valueType); - } - else if (auto arrayType = dynamic_cast<ArrayTypeName const*>(&_typeName)) - { - TypePointer baseType = typeFor(arrayType->baseType()); - if (baseType->storageBytes() == 0) - fatalTypeError(arrayType->baseType().location(), "Illegal base type of storage size zero for array."); - if (Expression const* length = arrayType->length()) - { - if (!length->annotation().type) - ConstantEvaluator e(*length); - - auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get()); - if (!lengthType) - fatalTypeError(length->location(), "Invalid array length."); - else - type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr)); - } - else - type = make_shared<ArrayType>(DataLocation::Storage, baseType); - } - - return _typeName.annotation().type = move(type); -} - void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description) { auto err = make_shared<Error>(Error::Type::TypeError); diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h index 6f5ced8c..a2d71dc3 100644 --- a/libsolidity/analysis/ReferencesResolver.h +++ b/libsolidity/analysis/ReferencesResolver.h @@ -60,13 +60,13 @@ public: private: virtual bool visit(Block const&) override { return m_resolveInsideCode; } virtual bool visit(Identifier const& _identifier) override; - virtual bool visit(UserDefinedTypeName const& _typeName) override; + virtual bool visit(ElementaryTypeName const& _typeName) override; + virtual void endVisit(UserDefinedTypeName const& _typeName) override; + virtual void endVisit(Mapping const& _typeName) override; + virtual void endVisit(ArrayTypeName const& _typeName) override; virtual bool visit(Return const& _return) override; - virtual void endVisit(NewExpression const& _new) override; virtual void endVisit(VariableDeclaration const& _variable) override; - TypePointer typeFor(TypeName const& _typeName); - /// Adds a new error to the list of errors. void typeError(SourceLocation const& _location, std::string const& _description); diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1d2d0258..851266bd 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -356,7 +356,16 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) " to " + parameterTypes[i]->toString() + " requested." - ); + ); +} + +void TypeChecker::endVisit(UsingForDirective const& _usingFor) +{ + ContractDefinition const* library = dynamic_cast<ContractDefinition const*>( + _usingFor.libraryName().annotation().referencedDeclaration + ); + if (!library || !library->isLibrary()) + typeError(_usingFor.libraryName().location(), "Library name expected."); } bool TypeChecker::visit(StructDefinition const& _struct) @@ -1119,7 +1128,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) for (auto it = possibleMembers.begin(); it != possibleMembers.end();) if ( it->type->category() == Type::Category::Function && - !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes) + !dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes, exprType) ) it = possibleMembers.erase(it); else @@ -1154,6 +1163,15 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) auto& annotation = _memberAccess.annotation(); annotation.referencedDeclaration = possibleMembers.front().declaration; annotation.type = possibleMembers.front().type; + + if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get())) + if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType())) + typeError( + _memberAccess.location(), + "Function \"" + memberName + "\" cannot be called on an object of type " + + exprType->toString() + " (expected " + funType->selfType()->toString() + ")" + ); + if (exprType->category() == Type::Category::Struct) annotation.isLValue = true; else if (exprType->category() == Type::Category::Array) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 9563d4a9..7829a23d 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -76,6 +76,7 @@ private: void checkLibraryRequirements(ContractDefinition const& _contract); virtual void endVisit(InheritanceSpecifier const& _inheritance) override; + virtual void endVisit(UsingForDirective const& _usingFor) override; virtual bool visit(StructDefinition const& _struct) override; virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(VariableDeclaration const& _variable) override; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a28d9f4f..1217d945 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -267,6 +267,7 @@ public: std::vector<ASTPointer<InheritanceSpecifier>> const& baseContracts() const { return m_baseContracts; } std::vector<ASTPointer<ASTNode>> const& subNodes() const { return m_subNodes; } + std::vector<UsingForDirective const*> usingForDirectives() const { return filteredNodes<UsingForDirective>(m_subNodes); } std::vector<StructDefinition const*> definedStructs() const { return filteredNodes<StructDefinition>(m_subNodes); } std::vector<EnumDefinition const*> definedEnums() const { return filteredNodes<EnumDefinition>(m_subNodes); } std::vector<VariableDeclaration const*> stateVariables() const { return filteredNodes<VariableDeclaration>(m_subNodes); } @@ -335,6 +336,33 @@ private: std::vector<ASTPointer<Expression>> m_arguments; }; +/** + * `using LibraryName for uint` will attach all functions from the library LibraryName + * to `uint` if the first parameter matches the type. `using LibraryName for *` attaches + * the function to any matching type. + */ +class UsingForDirective: public ASTNode +{ +public: + UsingForDirective( + SourceLocation const& _location, + ASTPointer<Identifier> const& _libraryName, + ASTPointer<TypeName> const& _typeName + ): + ASTNode(_location), m_libraryName(_libraryName), m_typeName(_typeName) {} + + virtual void accept(ASTVisitor& _visitor) override; + virtual void accept(ASTConstVisitor& _visitor) const override; + + Identifier const& libraryName() const { return *m_libraryName; } + /// @returns the type name the library is attached to, null for `*`. + TypeName const* typeName() const { return m_typeName.get(); } + +private: + ASTPointer<Identifier> m_libraryName; + ASTPointer<TypeName> m_typeName; +}; + class StructDefinition: public Declaration { public: diff --git a/libsolidity/ast/ASTForward.h b/libsolidity/ast/ASTForward.h index 02dd054a..6aaa77ce 100644 --- a/libsolidity/ast/ASTForward.h +++ b/libsolidity/ast/ASTForward.h @@ -39,6 +39,7 @@ class ImportDirective; class Declaration; class ContractDefinition; class InheritanceSpecifier; +class UsingForDirective; class StructDefinition; class EnumDefinition; class EnumValue; diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 14c09fb4..f04d9682 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -47,6 +47,7 @@ public: virtual bool visit(ImportDirective& _node) { return visitNode(_node); } virtual bool visit(ContractDefinition& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier& _node) { return visitNode(_node); } + virtual bool visit(UsingForDirective& _node) { return visitNode(_node); } virtual bool visit(StructDefinition& _node) { return visitNode(_node); } virtual bool visit(EnumDefinition& _node) { return visitNode(_node); } virtual bool visit(EnumValue& _node) { return visitNode(_node); } @@ -88,6 +89,7 @@ public: virtual void endVisit(ImportDirective& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier& _node) { endVisitNode(_node); } + virtual void endVisit(UsingForDirective& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition& _node) { endVisitNode(_node); } virtual void endVisit(EnumDefinition& _node) { endVisitNode(_node); } virtual void endVisit(EnumValue& _node) { endVisitNode(_node); } @@ -142,6 +144,7 @@ public: virtual bool visit(ContractDefinition const& _node) { return visitNode(_node); } virtual bool visit(InheritanceSpecifier const& _node) { return visitNode(_node); } virtual bool visit(StructDefinition const& _node) { return visitNode(_node); } + virtual bool visit(UsingForDirective const& _node) { return visitNode(_node); } virtual bool visit(EnumDefinition const& _node) { return visitNode(_node); } virtual bool visit(EnumValue const& _node) { return visitNode(_node); } virtual bool visit(ParameterList const& _node) { return visitNode(_node); } @@ -182,6 +185,7 @@ public: virtual void endVisit(ImportDirective const& _node) { endVisitNode(_node); } virtual void endVisit(ContractDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(InheritanceSpecifier const& _node) { endVisitNode(_node); } + virtual void endVisit(UsingForDirective const& _node) { endVisitNode(_node); } virtual void endVisit(StructDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(EnumDefinition const& _node) { endVisitNode(_node); } virtual void endVisit(EnumValue const& _node) { endVisitNode(_node); } diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h index f65595b8..61370c55 100644 --- a/libsolidity/ast/AST_accept.h +++ b/libsolidity/ast/AST_accept.h @@ -123,6 +123,28 @@ void EnumValue::accept(ASTConstVisitor& _visitor) const _visitor.endVisit(*this); } +void UsingForDirective::accept(ASTVisitor& _visitor) +{ + if (_visitor.visit(*this)) + { + m_libraryName->accept(_visitor); + if (m_typeName) + m_typeName->accept(_visitor); + } + _visitor.endVisit(*this); +} + +void UsingForDirective::accept(ASTConstVisitor& _visitor) const +{ + if (_visitor.visit(*this)) + { + m_libraryName->accept(_visitor); + if (m_typeName) + m_typeName->accept(_visitor); + } + _visitor.endVisit(*this); +} + void StructDefinition::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 96f44571..0409ac63 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -86,6 +86,11 @@ MemberList& MemberList::operator=(MemberList&& _other) return *this; } +void MemberList::combine(MemberList const & _other) +{ + m_memberTypes += _other.m_memberTypes; +} + std::pair<u256, unsigned> const* MemberList::memberStorageOffset(string const& _name) const { if (!m_storageOffsets) @@ -185,7 +190,52 @@ TypePointer Type::commonType(TypePointer const& _a, TypePointer const& _b) return TypePointer(); } -const MemberList Type::EmptyMemberList; +MemberList const& Type::members(ContractDefinition const* _currentScope) const +{ + if (!m_members[_currentScope]) + { + MemberList::MemberMap members = nativeMembers(_currentScope); + if (_currentScope) + members += boundFunctions(*this, *_currentScope); + m_members[_currentScope] = unique_ptr<MemberList>(new MemberList(move(members))); + } + return *m_members[_currentScope]; +} + +MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope) +{ + // Normalise data location of type. + TypePointer type = ReferenceType::copyForLocationIfReference(DataLocation::Storage, _type.shared_from_this()); + set<Declaration const*> seenFunctions; + MemberList::MemberMap members; + for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts) + for (UsingForDirective const* ufd: contract->usingForDirectives()) + { + if (ufd->typeName() && *type != *ReferenceType::copyForLocationIfReference( + DataLocation::Storage, + ufd->typeName()->annotation().type + )) + continue; + auto const& library = dynamic_cast<ContractDefinition const&>( + *ufd->libraryName().annotation().referencedDeclaration + ); + for (auto const& it: library.interfaceFunctions()) + { + FunctionType const& funType = *it.second; + solAssert(funType.hasDeclaration(), "Tried to bind function without declaration."); + if (seenFunctions.count(&funType.declaration())) + continue; + seenFunctions.insert(&funType.declaration()); + if (auto fun = funType.asMemberFunction(true, true)) + members.push_back(MemberList::Member( + funType.declaration().name(), + fun, + &funType.declaration() + )); + } + } + return members; +} IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): m_bits(_bits), m_modifier(_modifier) @@ -273,12 +323,18 @@ TypePointer IntegerType::binaryOperatorResult(Token::Value _operator, TypePointe return commonType; } -const MemberList IntegerType::AddressMemberList({ - {"balance", make_shared<IntegerType >(256)}, - {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, - {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, - {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} -}); +MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) const +{ + if (isAddress()) + return { + {"balance", make_shared<IntegerType >(256)}, + {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::Bare, true)}, + {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Location::BareCallCode, true)}, + {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Location::Send)} + }; + else + return MemberList::MemberMap(); +} bool IntegerConstantType::isValidLiteral(const Literal& _literal) { @@ -858,26 +914,22 @@ string ArrayType::canonicalName(bool _addDataLocation) const return ret; } -MemberList const& ArrayType::members(ContractDefinition const*) const +MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const { - if (!m_members) + MemberList::MemberMap members; + if (!isString()) { - MemberList::MemberMap members; - if (!isString()) - { - 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)}, - strings{string()}, - strings{string()}, - isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush - )}); - } - m_members.reset(new MemberList(members)); + 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)}, + strings{string()}, + strings{string()}, + isByteArray() ? FunctionType::Location::ByteArrayPush : FunctionType::Location::ArrayPush + )}); } - return *m_members; + return members; } TypePointer ArrayType::encodingType() const @@ -956,55 +1008,47 @@ string ContractType::canonicalName(bool) const return m_contract.annotation().canonicalName; } -MemberList const& ContractType::members(ContractDefinition const*) const +MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const*) const { - // We need to lazy-initialize it because of recursive references. - if (!m_members) + // All address members and all interface functions + MemberList::MemberMap members(IntegerType(120, IntegerType::Modifier::Address).nativeMembers(nullptr)); + if (m_super) { - // All address members and all interface functions - MemberList::MemberMap members( - IntegerType::AddressMemberList.begin(), - IntegerType::AddressMemberList.end() - ); - if (m_super) - { - // add the most derived of all functions which are visible in derived contracts - for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) - for (FunctionDefinition const* function: base->definedFunctions()) + // add the most derived of all functions which are visible in derived contracts + for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts) + for (FunctionDefinition const* function: base->definedFunctions()) + { + if (!function->isVisibleInDerivedContracts()) + continue; + auto functionType = make_shared<FunctionType>(*function, true); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) { - if (!function->isVisibleInDerivedContracts()) + if (member.name != function->name()) + continue; + auto memberType = dynamic_cast<FunctionType const*>(member.type.get()); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualArgumentTypes(*functionType)) continue; - auto functionType = make_shared<FunctionType>(*function, true); - bool functionWithEqualArgumentsFound = false; - for (auto const& member: members) - { - if (member.name != function->name()) - continue; - auto memberType = dynamic_cast<FunctionType const*>(member.type.get()); - solAssert(!!memberType, "Override changes type."); - if (!memberType->hasEqualArgumentTypes(*functionType)) - continue; - functionWithEqualArgumentsFound = true; - break; - } - if (!functionWithEqualArgumentsFound) - members.push_back(MemberList::Member( - function->name(), - functionType, - function - )); + functionWithEqualArgumentsFound = true; + break; } - } - else - for (auto const& it: m_contract.interfaceFunctions()) - members.push_back(MemberList::Member( - it.second->declaration().name(), - it.second->asMemberFunction(m_contract.isLibrary()), - &it.second->declaration() - )); - m_members.reset(new MemberList(members)); + if (!functionWithEqualArgumentsFound) + members.push_back(MemberList::Member( + function->name(), + functionType, + function + )); + } } - return *m_members; + else + for (auto const& it: m_contract.interfaceFunctions()) + members.push_back(MemberList::Member( + it.second->declaration().name(), + it.second->asMemberFunction(m_contract.isLibrary()), + &it.second->declaration() + )); + return members; } shared_ptr<FunctionType const> const& ContractType::constructorType() const @@ -1099,27 +1143,22 @@ string StructType::toString(bool _short) const return ret; } -MemberList const& StructType::members(ContractDefinition const*) const +MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const { - // We need to lazy-initialize it because of recursive references. - if (!m_members) + MemberList::MemberMap members; + for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) { - MemberList::MemberMap members; - for (ASTPointer<VariableDeclaration> const& variable: m_struct.members()) - { - TypePointer type = variable->annotation().type; - // Skip all mapping members if we are not in storage. - if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) - continue; - members.push_back(MemberList::Member( - variable->name(), - copyForLocationIfReference(type), - variable.get()) - ); - } - m_members.reset(new MemberList(members)); + TypePointer type = variable->annotation().type; + // Skip all mapping members if we are not in storage. + if (location() != DataLocation::Storage && !type->canLiveOutsideStorage()) + continue; + members.push_back(MemberList::Member( + variable->name(), + copyForLocationIfReference(type), + variable.get()) + ); } - return *m_members; + return members; } TypePointer StructType::interfaceType(bool _inLibrary) const @@ -1436,6 +1475,20 @@ FunctionType::FunctionType(const EventDefinition& _event): swap(paramNames, m_parameterNames); } +std::vector<string> FunctionType::parameterNames() const +{ + if (!bound()) + return m_parameterNames; + return vector<string>(m_parameterNames.cbegin() + 1, m_parameterNames.cend()); +} + +TypePointers FunctionType::parameterTypes() const +{ + if (!bound()) + return m_parameterTypes; + return TypePointers(m_parameterTypes.cbegin() + 1, m_parameterTypes.cend()); +} + bool FunctionType::operator==(Type const& _other) const { if (_other.category() != category()) @@ -1504,6 +1557,8 @@ unsigned FunctionType::sizeOnStack() const size++; if (m_valueSet) size++; + if (bound()) + size += m_parameterTypes.front()->sizeOnStack(); return size; } @@ -1533,7 +1588,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const return make_shared<FunctionType>(paramTypes, retParamTypes, m_parameterNames, m_returnParameterNames, m_location, m_arbitraryParameters); } -MemberList const& FunctionType::members(ContractDefinition const*) const +MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) const { switch (m_location) { @@ -1544,52 +1599,53 @@ MemberList const& FunctionType::members(ContractDefinition const*) const case Location::RIPEMD160: case Location::Bare: case Location::BareCallCode: - if (!m_members) - { - MemberList::MemberMap members{ - { - "value", + { + MemberList::MemberMap members{ + { + "value", + make_shared<FunctionType>( + parseElementaryTypeVector({"uint"}), + TypePointers{copyAndSetGasOrValue(false, true)}, + strings(), + strings(), + Location::SetValue, + false, + nullptr, + m_gasSet, + m_valueSet + ) + } + }; + if (m_location != Location::Creation) + members.push_back( + MemberList::Member( + "gas", make_shared<FunctionType>( parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(false, true)}, + TypePointers{copyAndSetGasOrValue(true, false)}, strings(), strings(), - Location::SetValue, + Location::SetGas, false, nullptr, m_gasSet, m_valueSet ) - } - }; - if (m_location != Location::Creation) - members.push_back( - MemberList::Member( - "gas", - make_shared<FunctionType>( - parseElementaryTypeVector({"uint"}), - TypePointers{copyAndSetGasOrValue(true, false)}, - strings(), - strings(), - Location::SetGas, - false, - nullptr, - m_gasSet, - m_valueSet - ) - ) - ); - m_members.reset(new MemberList(members)); - } - return *m_members; + ) + ); + return members; + } default: - return EmptyMemberList; + return MemberList::MemberMap(); } } -bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes) const +bool FunctionType::canTakeArguments(TypePointers const& _argumentTypes, TypePointer const& _selfType) const { - TypePointers const& paramTypes = parameterTypes(); + solAssert(!bound() || _selfType, ""); + if (bound() && !_selfType->isImplicitlyConvertibleTo(*selfType())) + return false; + TypePointers paramTypes = parameterTypes(); if (takesArbitraryParameters()) return true; else if (_argumentTypes.size() != paramTypes.size()) @@ -1678,11 +1734,12 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con m_arbitraryParameters, m_declaration, m_gasSet || _setGas, - m_valueSet || _setValue + m_valueSet || _setValue, + m_bound ); } -FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const +FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound) const { TypePointers parameterTypes; for (auto const& t: m_parameterTypes) @@ -1712,14 +1769,15 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary) const m_arbitraryParameters, m_declaration, m_gasSet, - m_valueSet + m_valueSet, + _bound ); } vector<string> const FunctionType::parameterTypeNames(bool _addDataLocation) const { vector<string> names; - for (TypePointer const& t: m_parameterTypes) + for (TypePointer const& t: parameterTypes()) names.push_back(t->canonicalName(_addDataLocation)); return names; @@ -1734,6 +1792,12 @@ vector<string> const FunctionType::returnParameterTypeNames(bool _addDataLocatio return names; } +TypePointer FunctionType::selfType() const +{ + solAssert(bound(), ""); + return m_parameterTypes.at(0); +} + ASTPointer<ASTString> FunctionType::documentation() const { auto function = dynamic_cast<Documented const*>(m_declaration); @@ -1784,43 +1848,37 @@ unsigned TypeType::sizeOnStack() const return 0; } -MemberList const& TypeType::members(ContractDefinition const* _currentScope) const +MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const { - // We need to lazy-initialize it because of recursive references. - if (!m_members || m_cachedScope != _currentScope) + MemberList::MemberMap members; + if (m_actualType->category() == Category::Contract) { - MemberList::MemberMap members; - if (m_actualType->category() == Category::Contract) - { - ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition(); - if (contract.isLibrary()) - for (auto const& it: contract.interfaceFunctions()) - members.push_back(MemberList::Member( - it.second->declaration().name(), - it.second->asMemberFunction(true), // use callcode - &it.second->declaration() - )); - else if (_currentScope != nullptr) - { - auto const& currentBases = _currentScope->annotation().linearizedBaseContracts; - if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) - // We are accessing the type of a base contract, so add all public and protected - // members. Note that this does not add inherited functions on purpose. - for (Declaration const* decl: contract.inheritableMembers()) - members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); - } - } - else if (m_actualType->category() == Category::Enum) + ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition(); + if (contract.isLibrary()) + for (auto const& it: contract.interfaceFunctions()) + members.push_back(MemberList::Member( + it.second->declaration().name(), + it.second->asMemberFunction(true), // use callcode + &it.second->declaration() + )); + else if (_currentScope != nullptr) { - EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition(); - auto enumType = make_shared<EnumType>(enumDef); - for (ASTPointer<EnumValue> const& enumValue: enumDef.members()) - members.push_back(MemberList::Member(enumValue->name(), enumType)); + auto const& currentBases = _currentScope->annotation().linearizedBaseContracts; + if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) + // We are accessing the type of a base contract, so add all public and protected + // members. Note that this does not add inherited functions on purpose. + for (Declaration const* decl: contract.inheritableMembers()) + members.push_back(MemberList::Member(decl->name(), decl->type(), decl)); } - m_members.reset(new MemberList(members)); - m_cachedScope = _currentScope; } - return *m_members; + else if (m_actualType->category() == Category::Enum) + { + EnumDefinition const& enumDef = dynamic_cast<EnumType const&>(*m_actualType).enumDefinition(); + auto enumType = make_shared<EnumType>(enumDef); + for (ASTPointer<EnumValue> const& enumValue: enumDef.members()) + members.push_back(MemberList::Member(enumValue->name(), enumType)); + } + return members; } ModifierType::ModifierType(const ModifierDefinition& _modifier) @@ -1866,10 +1924,22 @@ string ModifierType::toString(bool _short) const MagicType::MagicType(MagicType::Kind _kind): m_kind(_kind) { +} + +bool MagicType::operator==(Type const& _other) const +{ + if (_other.category() != category()) + return false; + MagicType const& other = dynamic_cast<MagicType const&>(_other); + return other.m_kind == m_kind; +} + +MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const +{ switch (m_kind) { case Kind::Block: - m_members = MemberList({ + return MemberList::MemberMap({ {"coinbase", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"timestamp", make_shared<IntegerType>(256)}, {"blockhash", make_shared<FunctionType>(strings{"uint"}, strings{"bytes32"}, FunctionType::Location::BlockHash)}, @@ -1877,35 +1947,24 @@ MagicType::MagicType(MagicType::Kind _kind): {"number", make_shared<IntegerType>(256)}, {"gaslimit", make_shared<IntegerType>(256)} }); - break; case Kind::Message: - m_members = MemberList({ + return MemberList::MemberMap({ {"sender", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"gas", make_shared<IntegerType>(256)}, {"value", make_shared<IntegerType>(256)}, {"data", make_shared<ArrayType>(DataLocation::CallData)}, {"sig", make_shared<FixedBytesType>(4)} }); - break; case Kind::Transaction: - m_members = MemberList({ + return MemberList::MemberMap({ {"origin", make_shared<IntegerType>(0, IntegerType::Modifier::Address)}, {"gasprice", make_shared<IntegerType>(256)} }); - break; default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic.")); } } -bool MagicType::operator==(Type const& _other) const -{ - if (_other.category() != category()) - return false; - MagicType const& other = dynamic_cast<MagicType const&>(_other); - return other.m_kind == m_kind; -} - string MagicType::toString(bool) const { switch (m_kind) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index ea9bd198..3ebcb2b2 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -90,6 +90,7 @@ public: 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 { TypePointer type; @@ -149,8 +150,6 @@ public: /// @returns a pointer to _a or _b if the other is implicitly convertible to it or nullptr otherwise static TypePointer commonType(TypePointer const& _a, TypePointer const& _b); - /// Calculates the - virtual Category category() const = 0; virtual bool isImplicitlyConvertibleTo(Type const& _other) const { return *this == _other; } virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const @@ -216,9 +215,9 @@ public: return _targetType->dataStoredIn(DataLocation::Storage) ? mobileType() : _targetType; } - /// Returns the list of all members of this type. Default implementation: no members. + /// Returns the list of all members of this type. Default implementation: no members apart from bound. /// @param _currentScope scope in which the members are accessed. - virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const { return EmptyMemberList; } + MemberList const& members(ContractDefinition const* _currentScope) const; /// Convenience method, returns the type of the given named member or an empty pointer if no such member exists. TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const { @@ -251,9 +250,20 @@ public: /// are returned without modification. virtual TypePointer interfaceType(bool /*_inLibrary*/) const { return TypePointer(); } +private: + /// @returns a member list containing all members added to this type by `using for` directives. + static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope); + protected: - /// Convenience object used when returning an empty member list. - static const MemberList EmptyMemberList; + /// @returns the members native to this type depending on the given context. This function + /// is used (in conjunction with boundFunctions to fill m_members below. + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const + { + return MemberList::MemberMap(); + } + + /// List of member types (parameterised by scape), will be lazy-initialized. + mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members; }; /** @@ -281,10 +291,7 @@ public: virtual unsigned storageBytes() const override { return m_bits / 8; } virtual bool isValueType() const override { return true; } - virtual MemberList const& members(ContractDefinition const* /*_currentScope*/) const override - { - return isAddress() ? AddressMemberList : EmptyMemberList; - } + virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual std::string toString(bool _short) const override; @@ -295,8 +302,6 @@ public: bool isAddress() const { return m_modifier == Modifier::Address; } bool isSigned() const { return m_modifier == Modifier::Signed; } - static const MemberList AddressMemberList; - private: int m_bits; Modifier m_modifier; @@ -517,7 +522,7 @@ public: virtual unsigned sizeOnStack() const override; virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; - virtual MemberList const& members(ContractDefinition const* _currentScope) const override; + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override; virtual TypePointer decodingType() const override; virtual TypePointer interfaceType(bool _inLibrary) const override; @@ -541,8 +546,6 @@ private: TypePointer m_baseType; bool m_hasDynamicLength = true; u256 m_length; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr<MemberList> m_members; }; /** @@ -570,7 +573,7 @@ public: virtual std::string toString(bool _short) const override; virtual std::string canonicalName(bool _addDataLocation) const override; - virtual MemberList const& members(ContractDefinition const* _currentScope) const override; + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { return std::make_shared<IntegerType>(160, IntegerType::Modifier::Address); @@ -602,8 +605,6 @@ private: bool m_super = false; /// Type of the constructor, @see constructorType. Lazily initialized. mutable FunctionTypePointer m_constructorType; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr<MemberList> m_members; }; /** @@ -623,7 +624,7 @@ public: virtual bool canLiveOutsideStorage() const override { return true; } virtual std::string toString(bool _short) const override; - virtual MemberList const& members(ContractDefinition const* _currentScope) const override; + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; virtual TypePointer encodingType() const override { return location() == DataLocation::Storage ? std::make_shared<IntegerType>(256) : TypePointer(); @@ -648,8 +649,6 @@ public: private: StructDefinition const& m_struct; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr<MemberList> m_members; }; /** @@ -688,8 +687,6 @@ public: private: EnumDefinition const& m_enum; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr<MemberList> m_members; }; /** @@ -791,7 +788,8 @@ public: bool _arbitraryParameters = false, Declaration const* _declaration = nullptr, bool _gasSet = false, - bool _valueSet = false + bool _valueSet = false, + bool _bound = false ): m_parameterTypes(_parameterTypes), m_returnParameterTypes(_returnParameterTypes), @@ -801,15 +799,18 @@ public: m_arbitraryParameters(_arbitraryParameters), m_gasSet(_gasSet), m_valueSet(_valueSet), + m_bound(_bound), m_declaration(_declaration) {} - TypePointers const& parameterTypes() const { return m_parameterTypes; } - std::vector<std::string> const& parameterNames() const { return m_parameterNames; } + TypePointers parameterTypes() const; + std::vector<std::string> parameterNames() const; std::vector<std::string> const parameterTypeNames(bool _addDataLocation) const; TypePointers const& returnParameterTypes() const { return m_returnParameterTypes; } std::vector<std::string> const& returnParameterNames() const { return m_returnParameterNames; } std::vector<std::string> const returnParameterTypeNames(bool _addDataLocation) const; + /// @returns the "self" parameter type for a bound function + TypePointer selfType() const; virtual bool operator==(Type const& _other) const override; virtual std::string toString(bool _short) const override; @@ -817,7 +818,7 @@ public: virtual u256 storageSize() const override; virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; - virtual MemberList const& members(ContractDefinition const* _currentScope) const override; + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; /// @returns TypePointer of a new FunctionType object. All input/return parameters are an /// appropriate external types (i.e. the interfaceType()s) of input/return parameters of @@ -828,7 +829,9 @@ public: /// @returns true if this function can take the given argument types (possibly /// after implicit conversion). - bool canTakeArguments(TypePointers const& _arguments) const; + /// @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; @@ -855,6 +858,7 @@ public: bool takesArbitraryParameters() const { return m_arbitraryParameters; } bool gasSet() const { return m_gasSet; } bool valueSet() const { return m_valueSet; } + bool bound() const { return m_bound; } /// @returns a copy of this type, where gas or value are set manually. This will never set one /// of the parameters to fals. @@ -865,7 +869,8 @@ public: /// This is needed if external functions are called on other contracts, as they cannot return /// dynamic values. /// @param _inLibrary if true, uses CallCode as location. - FunctionTypePointer asMemberFunction(bool _inLibrary) const; + /// @param _bound if true, the argumenst are placed as `arg1.functionName(arg2, ..., argn)`. + FunctionTypePointer asMemberFunction(bool _inLibrary, bool _bound = false) const; private: static TypePointers parseElementaryTypeVector(strings const& _types); @@ -879,8 +884,8 @@ private: bool const m_arbitraryParameters = false; bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack + bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn) bool m_isConstant = false; - mutable std::unique_ptr<MemberList> m_members; Declaration const* m_declaration = nullptr; }; @@ -935,13 +940,10 @@ public: virtual bool canLiveOutsideStorage() const override { return false; } virtual unsigned sizeOnStack() const override; virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; } - virtual MemberList const& members(ContractDefinition const* _currentScope) const override; + virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override; private: TypePointer m_actualType; - /// List of member types, will be lazy-initialized because of recursive references. - mutable std::unique_ptr<MemberList> m_members; - mutable ContractDefinition const* m_cachedScope = nullptr; }; @@ -988,14 +990,12 @@ public: virtual bool canBeStored() const override { return false; } virtual bool canLiveOutsideStorage() const override { return true; } virtual unsigned sizeOnStack() const override { return 0; } - virtual MemberList const& members(ContractDefinition const*) const override { return m_members; } + virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override; virtual std::string toString(bool _short) const override; private: Kind m_kind; - - MemberList m_members; }; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d4140ae8..fa077036 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -672,7 +672,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) _functionCall.expression().accept(*this); solAssert(function.parameterTypes().size() == 1, ""); solAssert(!!function.parameterTypes()[0], ""); - TypePointer const& paramType = function.parameterTypes()[0]; + TypePointer paramType = function.parameterTypes()[0]; shared_ptr<ArrayType> arrayType = function.location() == Location::ArrayPush ? make_shared<ArrayType>(DataLocation::Storage, paramType) : diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 9272eb4b..2b886121 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -168,6 +168,8 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary) subNodes.push_back(parseModifierDefinition()); else if (currentTokenValue == Token::Event) subNodes.push_back(parseEventDefinition()); + else if (currentTokenValue == Token::Using) + subNodes.push_back(parseUsingDirective()); else fatalParserError(std::string("Function, variable, struct or modifier declaration expected.")); } @@ -475,6 +477,24 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition() return nodeFactory.createNode<EventDefinition>(name, docstring, parameters, anonymous); } +ASTPointer<UsingForDirective> Parser::parseUsingDirective() +{ + ASTNodeFactory nodeFactory(*this); + + expectToken(Token::Using); + //@todo this should actually parse a full path. + ASTPointer<Identifier> library(parseIdentifier()); + ASTPointer<TypeName> typeName; + expectToken(Token::For); + if (m_scanner->currentToken() == Token::Mul) + m_scanner->next(); + else + typeName = parseTypeName(false); + nodeFactory.markEndPosition(); + expectToken(Token::Semicolon); + return nodeFactory.createNode<UsingForDirective>(library, typeName); +} + ASTPointer<ModifierInvocation> Parser::parseModifierInvocation() { ASTNodeFactory nodeFactory(*this); diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 663c0f92..5ff46242 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -74,6 +74,7 @@ private: ); ASTPointer<ModifierDefinition> parseModifierDefinition(); ASTPointer<EventDefinition> parseEventDefinition(); + ASTPointer<UsingForDirective> parseUsingDirective(); ASTPointer<ModifierInvocation> parseModifierInvocation(); ASTPointer<Identifier> parseIdentifier(); ASTPointer<TypeName> parseTypeName(bool _allowVar); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 4f26fa4d..73a9b660 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2529,6 +2529,166 @@ BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) BOOST_CHECK(success(text)); } +BOOST_AUTO_TEST_CASE(using_for_library) +{ + char const* text = R"( + library D { } + contract C { + using D for uint; + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_not_library) +{ + char const* text = R"( + contract D { } + contract C { + using D for uint; + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(using_for_function_exists) +{ + char const* text = R"( + library D { function double(uint self) returns (uint) { return 2*self; } } + contract C { + using D for uint; + function f(uint a) { + a.double; + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_function_on_int) +{ + char const* text = R"( + library D { function double(uint self) returns (uint) { return 2*self; } } + contract C { + using D for uint; + function f(uint a) returns (uint) { + return a.double(); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_function_on_struct) +{ + char const* text = R"( + library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s x; + function f(uint a) returns (uint) { + return x.mul(a); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_overload) +{ + char const* text = R"( + library D { + struct s { uint a; } + function mul(s storage self, uint x) returns (uint) { return self.a *= x; } + function mul(s storage self, bytes32 x) returns (bytes32) { } + } + contract C { + using D for D.s; + D.s x; + function f(uint a) returns (uint) { + return x.mul(a); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_by_name) +{ + char const* text = R"( + library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s x; + function f(uint a) returns (uint) { + return x.mul({x: a}); + } + } + )"; + BOOST_CHECK(success(text)); +} + +BOOST_AUTO_TEST_CASE(using_for_mismatch) +{ + char const* text = R"( + library D { function double(bytes32 self) returns (uint) { return 2; } } + contract C { + using D for uint; + function f(uint a) returns (uint) { + return a.double(); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(using_for_not_used) +{ + // This is an error because the function is only bound to uint. + // Had it been bound to *, it would have worked. + char const* text = R"( + library D { function double(uint self) returns (uint) { return 2; } } + contract C { + using D for uint; + function f(uint16 a) returns (uint) { + return a.double(); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(using_for_arbitrary_mismatch) +{ + // Bound to a, but self type does not match. + char const* text = R"( + library D { function double(bytes32 self) returns (uint) { return 2; } } + contract C { + using D for *; + function f(uint a) returns (uint) { + return a.double(); + } + } + )"; + BOOST_CHECK(expectError(text) == Error::Type::TypeError); +} + +BOOST_AUTO_TEST_CASE(bound_function_in_var) +{ + char const* text = R"( + library D { struct s { uint a; } function mul(s storage self, uint x) returns (uint) { return self.a *= x; } } + contract C { + using D for D.s; + D.s x; + function f(uint a) returns (uint) { + var g = x.mul; + return g({x: a}); + } + } + )"; + BOOST_CHECK(success(text)); +} + BOOST_AUTO_TEST_CASE(create_memory_arrays) { char const* text = R"( diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 7451397e..fd9076c3 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1032,6 +1032,21 @@ BOOST_AUTO_TEST_CASE(member_access_parser_ambiguity) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(using_for) +{ + char const* text = R"( + contract C { + struct s { uint a; } + using LibraryName for uint; + using Library2 for *; + using Lib for s; + function f() { + } + } + )"; + BOOST_CHECK(successParse(text)); +} + BOOST_AUTO_TEST_SUITE_END() } |