aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md3
-rw-r--r--docs/contracts.rst29
-rw-r--r--libsolidity/analysis/TypeChecker.cpp35
-rw-r--r--libsolidity/analysis/TypeChecker.h1
-rw-r--r--libsolidity/ast/AST.h12
-rw-r--r--libsolidity/parsing/Parser.cpp26
-rw-r--r--libsolidity/parsing/Parser.h3
-rw-r--r--libsolidity/parsing/Token.h2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp35
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp145
-rw-r--r--test/libsolidity/SolidityParser.cpp9
-rw-r--r--test/libsolidity/SolidityTypes.cpp2
12 files changed, 288 insertions, 14 deletions
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:
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 2ee04675..28c003bd 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -922,6 +922,35 @@ 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 constructor.
+#. Cannot define variables.
+#. Cannot define structs.
+#. Cannot define enums.
+
+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:
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 34ed8129..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<ASTNode const*> 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
@@ -354,6 +358,9 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
auto base = dynamic_cast<ContractDefinition const*>(&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 +403,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<VariableDeclaration> const& member: _struct.members())
if (!type(*member)->canBeStored())
typeError(member->location(), "Type cannot be used in struct.");
@@ -451,6 +461,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
vector<ContractDefinition const*>()
);
+ if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface)
+ {
+ if (_function.isImplemented())
+ typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
+ if (_function.visibility() < FunctionDefinition::Visibility::Public)
+ typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
+ if (_function.isConstructor())
+ typeError(_function.location(), "Constructor cannot be defined in interfaces.");
+ }
if (_function.isImplemented())
_function.body().accept(*this);
return false;
@@ -458,6 +477,9 @@ 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 declared 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
@@ -504,6 +526,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<ContractDefinition const*> 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<ContractDefinition const*> const& _bases);
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<ASTString> const& _name,
ASTPointer<ASTString> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> 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<FunctionDefinition const*> definedFunctions() const { return filteredNodes<FunctionDefinition>(m_subNodes); }
std::vector<EventDefinition const*> events() const { return filteredNodes<EventDefinition>(m_subNodes); }
std::vector<EventDefinition const*> 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<ASTPointer<InheritanceSpecifier>> m_baseContracts;
std::vector<ASTPointer<ASTNode>> 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..b5130c8a 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -82,9 +82,10 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> 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,30 @@ ASTPointer<ImportDirective> Parser::parseImportDirective()
return nodeFactory.createNode<ImportDirective>(path, unitAlias, move(symbolAliases));
}
-ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
+ContractDefinition::ContractKind Parser::tokenToContractKind(Token::Value _token)
+{
+ switch(_token)
+ {
+ case Token::Interface:
+ return ContractDefinition::ContractKind::Interface;
+ case Token::Contract:
+ return ContractDefinition::ContractKind::Contract;
+ case Token::Library:
+ return ContractDefinition::ContractKind::Library;
+ default:
+ fatalParserError("Unsupported contract type.");
+ }
+ // FIXME: fatalParserError is not considered as throwing here
+ return ContractDefinition::ContractKind::Contract;
+}
+
+ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _expectedKind)
{
ASTNodeFactory nodeFactory(*this);
ASTPointer<ASTString> docString;
if (m_scanner->currentCommentLiteral() != "")
docString = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- expectToken(_isLibrary ? Token::Library : Token::Contract);
+ expectToken(_expectedKind);
ASTPointer<ASTString> name = expectIdentifierToken();
vector<ASTPointer<InheritanceSpecifier>> baseContracts;
if (m_scanner->currentToken() == Token::Is)
@@ -252,7 +270,7 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(bool _isLibrary)
docString,
baseContracts,
subNodes,
- _isLibrary
+ tokenToContractKind(_expectedKind)
);
}
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 79d1d1d4..282617ab 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -69,7 +69,8 @@ private:
///@name Parsing functions for the AST nodes
ASTPointer<PragmaDirective> parsePragmaDirective();
ASTPointer<ImportDirective> parseImportDirective();
- ASTPointer<ContractDefinition> parseContractDefinition(bool _isLibrary);
+ ContractDefinition::ContractKind tokenToContractKind(Token::Value _token);
+ ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind);
ASTPointer<InheritanceSpecifier> 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) \
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 fa310434..c002fd3e 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -5327,6 +5327,151 @@ 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();
+ }
+ )";
+ CHECK_ERROR(text, TypeError, "Constructor cannot be defined in interfaces");
+}
+
+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_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"(
+ 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 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"(
+ interface I {
+ function f();
+ }
+ contract C is I {
+ function f() {
+ }
+ }
+ )";
+ 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()
}
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()
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<ArrayType>(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<string>("MyContract$"), {}, {}, {}, false);
+ ContractDefinition c(SourceLocation{}, make_shared<string>("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");