diff --git a/Changelog.md b/Changelog.md index 7e0e56185..732c0a7f3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -75,6 +75,7 @@ Language Features: * General: Scoping rules now follow the C99-style. * General: Allow ``enum``s in interfaces. * General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions. + * General: Allow ``struct``s in interfaces. Compiler Features: * C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 0ffe66368..601a5e7c2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -580,9 +580,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) bool TypeChecker::visit(StructDefinition const& _struct) { - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces."); - for (ASTPointer const& member: _struct.members()) if (!type(*member)->canBeStored()) m_errorReporter.typeError(member->location(), "Type cannot be used in struct."); @@ -610,7 +607,10 @@ bool TypeChecker::visit(StructDefinition const& _struct) if (CycleDetector(visitor).run(_struct) != nullptr) m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition."); + bool insideStruct = true; + swap(insideStruct, m_insideStruct); ASTNode::listAccept(_struct.members(), *this); + m_insideStruct = insideStruct; return false; } @@ -693,10 +693,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { // Forbid any variable declarations inside interfaces unless they are part of - // a function's input/output parameters. + // * a function's input/output parameters, + // * or inside of a struct definition. if ( m_scope->contractKind() == ContractDefinition::ContractKind::Interface && !_variable.isCallableParameter() + && !m_insideStruct ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index b696de851..ed316f9c2 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -149,6 +149,9 @@ private: /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; + /// Flag indicating whether we are currently inside a StructDefinition. + bool m_insideStruct = false; + ErrorReporter& m_errorReporter; }; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index d8e95ad2d..7b56fa9da 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -4755,6 +4755,68 @@ BOOST_AUTO_TEST_CASE(constructing_enums_from_ints) ABI_CHECK(callContractFunction("test()"), encodeArgs(1)); } +BOOST_AUTO_TEST_CASE(struct_referencing) +{ + static char const* sourceCode = R"( + pragma experimental ABIEncoderV2; + interface I { + struct S { uint a; } + } + library L { + struct S { uint b; uint a; } + function f() public pure returns (S) { + S memory s; + s.a = 3; + return s; + } + function g() public pure returns (I.S) { + I.S memory s; + s.a = 4; + return s; + } + // argument-dependant lookup tests + function a(I.S memory) public pure returns (uint) { return 1; } + function a(S memory) public pure returns (uint) { return 2; } + } + contract C is I { + function f() public pure returns (S) { + S memory s; + s.a = 1; + return s; + } + function g() public pure returns (I.S) { + I.S memory s; + s.a = 2; + return s; + } + function h() public pure returns (L.S) { + L.S memory s; + s.a = 5; + return s; + } + function x() public pure returns (L.S) { + return L.f(); + } + function y() public pure returns (I.S) { + return L.g(); + } + function a1() public pure returns (uint) { S memory s; return L.a(s); } + function a2() public pure returns (uint) { L.S memory s; return L.a(s); } + } + )"; + compileAndRun(sourceCode, 0, "L"); + ABI_CHECK(callContractFunction("f()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(4)); + compileAndRun(sourceCode, 0, "C", bytes(), map{ {"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(2)); + ABI_CHECK(callContractFunction("h()"), encodeArgs(0, 5)); + ABI_CHECK(callContractFunction("x()"), encodeArgs(0, 3)); + ABI_CHECK(callContractFunction("y()"), encodeArgs(4)); + ABI_CHECK(callContractFunction("a1()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("a2()"), encodeArgs(2)); +} + BOOST_AUTO_TEST_CASE(enum_referencing) { char const* sourceCode = R"( diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol index c74d52d3c..385ed18e0 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/419_interface_structs.sol @@ -1,9 +1,6 @@ interface I { struct A { - // This is currently expected to break, but it *may* change in the future. int dummy; } } // ---- -// TypeError: (18-136): Structs cannot be defined in interfaces. -// TypeError: (120-129): Variables cannot be declared in interfaces.