From 6d4628ace4082a4318dc61fff49e8d193eea36e9 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 7 Feb 2017 22:11:50 +0000 Subject: Add ContractKind to ContractDefinition --- libsolidity/ast/AST.h | 12 ++++++++---- libsolidity/parsing/Parser.cpp | 2 +- test/libsolidity/SolidityTypes.cpp | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 8031760d..02234ffc 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -316,19 +316,21 @@ protected: class ContractDefinition: public Declaration, public Documented { public: + enum class ContractKind { Interface, Contract, Library }; + ContractDefinition( SourceLocation const& _location, ASTPointer const& _name, ASTPointer const& _documentation, std::vector> const& _baseContracts, std::vector> const& _subNodes, - bool _isLibrary + ContractKind _contractKind = ContractKind::Contract ): Declaration(_location, _name), Documented(_documentation), m_baseContracts(_baseContracts), m_subNodes(_subNodes), - m_isLibrary(_isLibrary) + m_contractKind(_contractKind) {} virtual void accept(ASTVisitor& _visitor) override; @@ -344,7 +346,7 @@ public: std::vector definedFunctions() const { return filteredNodes(m_subNodes); } std::vector events() const { return filteredNodes(m_subNodes); } std::vector const& interfaceEvents() const; - bool isLibrary() const { return m_isLibrary; } + bool isLibrary() const { return m_contractKind == ContractKind::Library; } /// @returns a map of canonical function signatures to FunctionDefinitions /// as intended for use by the ABI. @@ -371,10 +373,12 @@ public: virtual ContractDefinitionAnnotation& annotation() const override; + ContractKind contractKind() const { return m_contractKind; } + private: std::vector> m_baseContracts; std::vector> m_subNodes; - bool m_isLibrary; + ContractKind m_contractKind; // parsed Natspec documentation of the contract. Json::Value m_userDocumentation; diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index e26e2908..acd87d55 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -252,7 +252,7 @@ ASTPointer Parser::parseContractDefinition(bool _isLibrary) docString, baseContracts, subNodes, - _isLibrary + _isLibrary ? ContractDefinition::ContractKind::Library : ContractDefinition::ContractKind::Contract ); } diff --git a/test/libsolidity/SolidityTypes.cpp b/test/libsolidity/SolidityTypes.cpp index 5362239d..0b5ab516 100644 --- a/test/libsolidity/SolidityTypes.cpp +++ b/test/libsolidity/SolidityTypes.cpp @@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE(type_identifiers) TypePointer multiArray = make_shared(DataLocation::Storage, stringArray); BOOST_CHECK_EQUAL(multiArray->identifier(), "t_array$_t_array$_t_string_storage_$20_storage_$dyn_storage_ptr"); - ContractDefinition c(SourceLocation{}, make_shared("MyContract$"), {}, {}, {}, false); + ContractDefinition c(SourceLocation{}, make_shared("MyContract$"), {}, {}, {}, ContractDefinition::ContractKind::Contract); BOOST_CHECK_EQUAL(c.type()->identifier(), "t_type$_t_contract$_MyContract$$$_$2_$"); BOOST_CHECK_EQUAL(ContractType(c, true).identifier(), "t_super$_MyContract$$$_$2"); -- cgit From 54230d2d5d15638fdf7c3893ee436fd8bc380e8c Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 7 Feb 2017 22:12:38 +0000 Subject: Support interface (contract) keyword in the parser --- libsolidity/parsing/Parser.cpp | 24 ++++++++++++++++++++---- libsolidity/parsing/Parser.h | 2 +- libsolidity/parsing/Token.h | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index acd87d55..4cf6f0a6 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -82,9 +82,10 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) case Token::Import: nodes.push_back(parseImportDirective()); break; + case Token::Interface: case Token::Contract: case Token::Library: - nodes.push_back(parseContractDefinition(token == Token::Library)); + nodes.push_back(parseContractDefinition(token)); break; default: fatalParserError(string("Expected import directive or contract definition.")); @@ -193,13 +194,28 @@ ASTPointer Parser::parseImportDirective() return nodeFactory.createNode(path, unitAlias, move(symbolAliases)); } -ASTPointer Parser::parseContractDefinition(bool _isLibrary) +ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) { ASTNodeFactory nodeFactory(*this); ASTPointer docString; if (m_scanner->currentCommentLiteral() != "") docString = make_shared(m_scanner->currentCommentLiteral()); - expectToken(_isLibrary ? Token::Library : Token::Contract); + expectToken(_expectedKind); + ContractDefinition::ContractKind contractKind; + switch(_expectedKind) + { + case Token::Interface: + contractKind = ContractDefinition::ContractKind::Interface; + break; + case Token::Contract: + contractKind = ContractDefinition::ContractKind::Contract; + break; + case Token::Library: + contractKind = ContractDefinition::ContractKind::Library; + break; + default: + fatalParserError("Unsupported contract type."); + } ASTPointer name = expectIdentifierToken(); vector> baseContracts; if (m_scanner->currentToken() == Token::Is) @@ -252,7 +268,7 @@ ASTPointer Parser::parseContractDefinition(bool _isLibrary) docString, baseContracts, subNodes, - _isLibrary ? ContractDefinition::ContractKind::Library : ContractDefinition::ContractKind::Contract + contractKind ); } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index 79d1d1d4..bf972c65 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -69,7 +69,7 @@ private: ///@name Parsing functions for the AST nodes ASTPointer parsePragmaDirective(); ASTPointer parseImportDirective(); - ASTPointer parseContractDefinition(bool _isLibrary); + ASTPointer parseContractDefinition(Token::Value _expectedKind); ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index c6d050bb..9a557ebd 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -157,6 +157,7 @@ namespace solidity K(Hex, "hex", 0) \ K(If, "if", 0) \ K(Indexed, "indexed", 0) \ + K(Interface, "interface", 0) \ K(Internal, "internal", 0) \ K(Import, "import", 0) \ K(Is, "is", 0) \ @@ -225,7 +226,6 @@ namespace solidity K(Final, "final", 0) \ K(In, "in", 0) \ K(Inline, "inline", 0) \ - K(Interface, "interface", 0) \ K(Let, "let", 0) \ K(Match, "match", 0) \ K(NullLiteral, "null", 0) \ -- cgit From 4693aed17744810387c887194f07ba1a969b0a05 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Tue, 7 Feb 2017 22:13:03 +0000 Subject: Reject invalid definitions for interface contracts --- libsolidity/analysis/TypeChecker.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 34ed8129..889b72b9 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -354,6 +354,9 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_inheritance.location(), "Interfaces cannot inherit."); + if (base->isLibrary()) typeError(_inheritance.location(), "Libraries cannot be inherited from."); @@ -396,6 +399,9 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) bool TypeChecker::visit(StructDefinition const& _struct) { + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_struct.location(), "Structs cannot be defined in interfaces."); + for (ASTPointer const& member: _struct.members()) if (!type(*member)->canBeStored()) typeError(member->location(), "Type cannot be used in struct."); @@ -452,12 +458,19 @@ bool TypeChecker::visit(FunctionDefinition const& _function) vector() ); if (_function.isImplemented()) + { + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_function.location(), "Functions in interfaces cannot have an implementation."); _function.body().accept(*this); + } return false; } bool TypeChecker::visit(VariableDeclaration const& _variable) { + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_variable.location(), "Variables cannot be defined in interfaces."); + // Variables can be declared without type (with "var"), in which case the first assignment // sets the type. // Note that assignments before the first declaration are legal because of the special scoping -- cgit From 4540daaf475490f0ea85d8cc0ec54c261b3eb5d6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 11 Feb 2017 03:14:21 +0000 Subject: Changelog --- Changelog.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.md b/Changelog.md index 99089b46..2a267fd7 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,8 @@ ### 0.4.11 (unreleased) +Features: + * Support ``interface`` contracts. + ### 0.4.10 (2017-03-15) Features: -- cgit From cfab70fd89e98f9301838682b28b9e400e1b2632 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sat, 11 Feb 2017 21:17:24 +0000 Subject: Add tests for interfaces --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 98 ++++++++++++++++++++++ test/libsolidity/SolidityParser.cpp | 9 ++ 2 files changed, 107 insertions(+) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index fa310434..0861dded 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5327,6 +5327,104 @@ BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(interface) +{ + char const* text = R"( + interface I { + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(interface_constructor) +{ + char const* text = R"( + interface I { + function I(); + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(interface_functions) +{ + char const* text = R"( + interface I { + function(); + function f(); + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(interface_function_bodies) +{ + char const* text = R"( + interface I { + function f() { + } + } + )"; + CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation"); +} + +BOOST_AUTO_TEST_CASE(interface_events) +{ + char const* text = R"( + interface I { + event E(); + } + )"; + success(text); +} + +BOOST_AUTO_TEST_CASE(interface_inheritance) +{ + char const* text = R"( + interface A { + } + interface I is A { + } + )"; + CHECK_ERROR(text, TypeError, "Interfaces cannot inherit"); +} + + +BOOST_AUTO_TEST_CASE(interface_structs) +{ + char const* text = R"( + interface I { + struct A { + } + } + )"; + CHECK_ERROR(text, TypeError, "Structs cannot be defined in interfaces"); +} + +BOOST_AUTO_TEST_CASE(interface_variables) +{ + char const* text = R"( + interface I { + uint a; + } + )"; + CHECK_ERROR(text, TypeError, "Variables cannot be defined in interfaces"); +} + +BOOST_AUTO_TEST_CASE(using_interface) +{ + char const* text = R"( + interface I { + function f(); + } + contract C is I { + function f() { + } + } + )"; + success(text); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index ffb4b6f2..6e33aba5 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1493,6 +1493,15 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(interface) +{ + char const* text = R"( + interface Interface { + function f(); + } + )"; + BOOST_CHECK(successParse(text)); +} BOOST_AUTO_TEST_SUITE_END() -- cgit From 3f1468142badd2717596a4cd78f6f347b78010d7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Sun, 12 Feb 2017 15:21:32 +0000 Subject: Document interfaces --- docs/contracts.rst | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 2ee04675..33627b92 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -922,6 +922,33 @@ Such contracts cannot be compiled (even if they contain implemented functions al If a contract inherits from an abstract contract and does not implement all non-implemented functions by overriding, it will itself be abstract. +.. index:: ! contract;interface, ! interface contract + +********** +Interfaces +********** + +Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: + +#. Cannot inherit other contracts or interfaces. +#. Cannot define variables. +#. Cannot define structs. + +Some of these restrictions might be lifted in the future. + +Interfaces are basically limited to what the Contract ABI can represent and the conversion between the ABI and +an Interface should be possible without any information loss. + +Interfaces are denoted by their own keyword: + +:: + + interface Token { + function transfer(address recipient, uint amount); + } + +Contracts can inherit interfaces as they would inherit other contracts. + .. index:: ! library, callcode, delegatecall .. _libraries: -- cgit From f8da9a8fd5e2c709aa940402155faa30139ec9e1 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Feb 2017 11:40:29 +0000 Subject: Factor out tokenToContractKind --- libsolidity/parsing/Parser.cpp | 32 +++++++++++++++++--------------- libsolidity/parsing/Parser.h | 1 + 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 4cf6f0a6..b5130c8a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -194,28 +194,30 @@ ASTPointer Parser::parseImportDirective() return nodeFactory.createNode(path, unitAlias, move(symbolAliases)); } -ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) +ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token) { - ASTNodeFactory nodeFactory(*this); - ASTPointer docString; - if (m_scanner->currentCommentLiteral() != "") - docString = make_shared(m_scanner->currentCommentLiteral()); - expectToken(_expectedKind); - ContractDefinition::ContractKind contractKind; - switch(_expectedKind) + switch(_token) { case Token::Interface: - contractKind = ContractDefinition::ContractKind::Interface; - break; + return ContractDefinition::ContractKind::Interface; case Token::Contract: - contractKind = ContractDefinition::ContractKind::Contract; - break; + return ContractDefinition::ContractKind::Contract; case Token::Library: - contractKind = ContractDefinition::ContractKind::Library; - break; + return ContractDefinition::ContractKind::Library; default: fatalParserError("Unsupported contract type."); } + // FIXME: fatalParserError is not considered as throwing here + return ContractDefinition::ContractKind::Contract; +} + +ASTPointer Parser::parseContractDefinition(Token::Value _expectedKind) +{ + ASTNodeFactory nodeFactory(*this); + ASTPointer docString; + if (m_scanner->currentCommentLiteral() != "") + docString = make_shared(m_scanner->currentCommentLiteral()); + expectToken(_expectedKind); ASTPointer name = expectIdentifierToken(); vector> baseContracts; if (m_scanner->currentToken() == Token::Is) @@ -268,7 +270,7 @@ ASTPointer Parser::parseContractDefinition(Token::Value _exp docString, baseContracts, subNodes, - contractKind + tokenToContractKind(_expectedKind) ); } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index bf972c65..282617ab 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -69,6 +69,7 @@ private: ///@name Parsing functions for the AST nodes ASTPointer parsePragmaDirective(); ASTPointer parseImportDirective(); + ContractDefinition::ContractKind tokenToContractKind(Token::Value _token); ASTPointer parseContractDefinition(Token::Value _expectedKind); ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); -- cgit From 16a91ef90aaf589361e34a10dcee2181ba6cb2ed Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Feb 2017 11:43:26 +0000 Subject: Use declared instead of defined --- libsolidity/analysis/TypeChecker.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 889b72b9..f6e4d6c8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -469,7 +469,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - typeError(_variable.location(), "Variables cannot be defined in interfaces."); + typeError(_variable.location(), "Variables cannot be declared in interfaces."); // Variables can be declared without type (with "var"), in which case the first assignment // sets the type. diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 0861dded..c30bb011 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5408,7 +5408,7 @@ BOOST_AUTO_TEST_CASE(interface_variables) uint a; } )"; - CHECK_ERROR(text, TypeError, "Variables cannot be defined in interfaces"); + CHECK_ERROR(text, TypeError, "Variables cannot be declared in interfaces"); } BOOST_AUTO_TEST_CASE(using_interface) -- cgit From 2c4bce2d62dc8bfc752858db12c625aec6e5960f Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 15 Mar 2017 22:12:31 +0000 Subject: Disallow enums in interfaces --- docs/contracts.rst | 1 + libsolidity/analysis/TypeChecker.cpp | 7 +++++++ libsolidity/analysis/TypeChecker.h | 1 + test/libsolidity/SolidityNameAndTypeResolution.cpp | 10 ++++++++++ 4 files changed, 19 insertions(+) diff --git a/docs/contracts.rst b/docs/contracts.rst index 33627b92..921d2870 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -933,6 +933,7 @@ Interfaces are similar to abstract contracts, but they cannot have any functions #. Cannot inherit other contracts or interfaces. #. Cannot define variables. #. Cannot define structs. +#. Cannot define enums. Some of these restrictions might be lifted in the future. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f6e4d6c8..3dffecdb 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -517,6 +517,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) return false; } +bool TypeChecker::visit(EnumDefinition const& _enum) +{ + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_enum.location(), "Enumerable cannot be declared in interfaces."); + return false; +} + void TypeChecker::visitManually( ModifierInvocation const& _modifier, vector const& _bases diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 46d8230a..88559f44 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -83,6 +83,7 @@ private: virtual bool visit(StructDefinition const& _struct) override; virtual bool visit(FunctionDefinition const& _function) override; virtual bool visit(VariableDeclaration const& _variable) override; + virtual bool visit(EnumDefinition const& _enum) override; /// We need to do this manually because we want to pass the bases of the current contract in /// case this is a base constructor call. void visitManually(ModifierInvocation const& _modifier, std::vector const& _bases); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index c30bb011..f6c875f1 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5411,6 +5411,16 @@ BOOST_AUTO_TEST_CASE(interface_variables) CHECK_ERROR(text, TypeError, "Variables cannot be declared in interfaces"); } +BOOST_AUTO_TEST_CASE(interface_enums) +{ + char const* text = R"( + interface I { + enum A { B, C } + } + )"; + CHECK_ERROR(text, TypeError, "Enumerable cannot be declared in interfaces"); +} + BOOST_AUTO_TEST_CASE(using_interface) { char const* text = R"( -- cgit From d5102c1db7cd2334e127ff684a6ecdd6aff156c6 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 17 Mar 2017 16:37:02 +0000 Subject: Disallow constructor in interfaces --- docs/contracts.rst | 1 + libsolidity/analysis/TypeChecker.cpp | 3 +++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/contracts.rst b/docs/contracts.rst index 921d2870..28c003bd 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -931,6 +931,7 @@ Interfaces Interfaces are similar to abstract contracts, but they cannot have any functions implemented. There are further restrictions: #. Cannot inherit other contracts or interfaces. +#. Cannot define constructor. #. Cannot define variables. #. Cannot define structs. #. Cannot define enums. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3dffecdb..87951003 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -463,6 +463,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) typeError(_function.location(), "Functions in interfaces cannot have an implementation."); _function.body().accept(*this); } + if (_function.isConstructor()) + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_function.location(), "Constructor cannot be defined in interfaces."); return false; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index f6c875f1..39306f84 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5343,7 +5343,7 @@ BOOST_AUTO_TEST_CASE(interface_constructor) function I(); } )"; - success(text); + CHECK_ERROR(text, TypeError, "Constructor cannot be defined in interfaces"); } BOOST_AUTO_TEST_CASE(interface_functions) -- cgit From 5a71e4f1a7e856960d326be1743736cc04d3c238 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 17 Mar 2017 16:55:13 +0000 Subject: Add more complex tests for interfaces --- test/libsolidity/SolidityEndToEndTest.cpp | 35 ++++++++++++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 17 +++++++++++ 2 files changed, 52 insertions(+) diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 7ef34383..8dd5042a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -9266,6 +9266,41 @@ BOOST_AUTO_TEST_CASE(scientific_notation) BOOST_CHECK(callContractFunction("k()") == encodeArgs(u256(-25))); } +BOOST_AUTO_TEST_CASE(interface) +{ + char const* sourceCode = R"( + interface I { + event A(); + function f() returns (bool); + function() payable; + } + + contract A is I { + function f() returns (bool) { + return g(); + } + + function g() returns (bool) { + return true; + } + + function() payable { + } + } + + contract C { + function f(address _interfaceAddress) returns (bool) { + I i = I(_interfaceAddress); + return i.f(); + } + } + )"; + compileAndRun(sourceCode, 0, "A"); + u160 const recipient = m_contractAddress; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(address)", recipient) == encodeArgs(true)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 39306f84..2a0f342c 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5435,6 +5435,23 @@ BOOST_AUTO_TEST_CASE(using_interface) success(text); } +BOOST_AUTO_TEST_CASE(using_interface_complex) +{ + char const* text = R"( + interface I { + event A(); + function f(); + function g(); + function(); + } + contract C is I { + function f() { + } + } + )"; + success(text); +} + BOOST_AUTO_TEST_SUITE_END() } -- cgit From 2067a00f2236980eeef812016ca923ec2cf3dbd2 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 17 Mar 2017 16:59:36 +0000 Subject: Disallow private or internal functions in interfaces --- libsolidity/analysis/TypeChecker.cpp | 3 +++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 87951003..3456958c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -463,6 +463,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) typeError(_function.location(), "Functions in interfaces cannot have an implementation."); _function.body().accept(*this); } + if (_function.visibility() < FunctionDefinition::Visibility::Public) + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + typeError(_function.location(), "Functions in interfaces cannot be internal or private."); if (_function.isConstructor()) if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) typeError(_function.location(), "Constructor cannot be defined in interfaces."); diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 2a0f342c..c002fd3e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -5368,6 +5368,26 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies) CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation"); } +BOOST_AUTO_TEST_CASE(interface_function_internal) +{ + char const* text = R"( + interface I { + function f() internal; + } + )"; + CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); +} + +BOOST_AUTO_TEST_CASE(interface_function_private) +{ + char const* text = R"( + interface I { + function f() private; + } + )"; + CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private."); +} + BOOST_AUTO_TEST_CASE(interface_events) { char const* text = R"( -- cgit From 96c09fcbcd21ed6b907e055c915d5f13814b87d5 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Fri, 17 Mar 2017 17:01:13 +0000 Subject: Simplify interface checks for FunctionDefinition --- libsolidity/analysis/TypeChecker.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3456958c..b0551fcb 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -457,18 +457,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function) dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts : vector() ); - if (_function.isImplemented()) + if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) { - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (_function.isImplemented()) typeError(_function.location(), "Functions in interfaces cannot have an implementation."); - _function.body().accept(*this); - } - if (_function.visibility() < FunctionDefinition::Visibility::Public) - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (_function.visibility() < FunctionDefinition::Visibility::Public) typeError(_function.location(), "Functions in interfaces cannot be internal or private."); - if (_function.isConstructor()) - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) + if (_function.isConstructor()) typeError(_function.location(), "Constructor cannot be defined in interfaces."); + } + if (_function.isImplemented()) + _function.body().accept(*this); return false; } -- cgit From 5ced3af3a0579e4e0d5e0d923b3e9d3f21197bfa Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 21 Mar 2017 15:05:59 +0100 Subject: Visit structs only once. --- libsolidity/analysis/TypeChecker.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index b0551fcb..30e84f11 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -64,8 +64,10 @@ bool TypeChecker::visit(ContractDefinition const& _contract) { m_scope = &_contract; - // We force our own visiting order here. - //@TODO structs will be visited again below, but it is probably fine. + // We force our own visiting order here. The structs have to be excluded below. + set visited; + for (auto const& s: _contract.definedStructs()) + visited.insert(s); ASTNode::listAccept(_contract.definedStructs(), *this); ASTNode::listAccept(_contract.baseContracts(), *this); @@ -113,7 +115,9 @@ bool TypeChecker::visit(ContractDefinition const& _contract) _contract.annotation().isFullyImplemented = false; } - ASTNode::listAccept(_contract.subNodes(), *this); + for (auto const& n: _contract.subNodes()) + if (!visited.count(n.get())) + n->accept(*this); checkContractExternalTypeClashes(_contract); // check for hash collisions in function signatures -- cgit