Browse Source

Allow structs containing mappings in memory.

cl-refactor
chriseth 10 years ago
parent
commit
8221a3c4cd
  1. 30
      libsolidity/AST.cpp
  2. 26
      libsolidity/Types.cpp
  3. 9
      libsolidity/Types.h
  4. 23
      test/libsolidity/SolidityEndToEndTest.cpp
  5. 15
      test/libsolidity/SolidityNameAndTypeResolution.cpp

30
libsolidity/AST.cpp

@ -830,11 +830,14 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
return; return;
} }
/// For error message: Struct members that were removed during conversion to memory.
set<string> membersRemovedForStructConstructor;
if (isStructConstructorCall()) if (isStructConstructorCall())
{ {
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType); TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType()); auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
functionType = structType.constructorType(); functionType = structType.constructorType();
membersRemovedForStructConstructor = structType.membersMissingInMemory();
} }
else else
functionType = dynamic_pointer_cast<FunctionType const>(expressionType); functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
@ -847,13 +850,22 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
// function parameters // function parameters
TypePointers const& parameterTypes = functionType->getParameterTypes(); TypePointers const& parameterTypes = functionType->getParameterTypes();
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(createTypeError( {
string msg =
"Wrong argument count for function call: " + "Wrong argument count for function call: " +
toString(m_arguments.size()) + toString(m_arguments.size()) +
" arguments given but expected " + " arguments given but expected " +
toString(parameterTypes.size()) + toString(parameterTypes.size()) +
"." ".";
)); // Extend error message in case we try to construct a struct with mapping member.
if (isStructConstructorCall() && !membersRemovedForStructConstructor.empty())
{
msg += " Members that have to be skipped in memory:";
for (auto const& member: membersRemovedForStructConstructor)
msg += " " + member;
}
BOOST_THROW_EXCEPTION(createTypeError(msg));
}
if (isPositionalCall) if (isPositionalCall)
{ {
@ -972,10 +984,22 @@ void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes)
++it; ++it;
} }
if (possibleMembers.size() == 0) if (possibleMembers.size() == 0)
{
auto storageType = ReferenceType::copyForLocationIfReference(
DataLocation::Storage,
m_expression->getType()
);
if (!storageType->getMembers().membersByName(*m_memberName).empty())
BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" is not available in " +
type.toString() +
" outside of storage."
));
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" not found or not visible " "Member \"" + *m_memberName + "\" not found or not visible "
"after argument-dependent lookup in " + type.toString() "after argument-dependent lookup in " + type.toString()
)); ));
}
else if (possibleMembers.size() > 1) else if (possibleMembers.size() > 1)
BOOST_THROW_EXCEPTION(createTypeError( BOOST_THROW_EXCEPTION(createTypeError(
"Member \"" + *m_memberName + "\" not unique " "Member \"" + *m_memberName + "\" not unique "

26
libsolidity/Types.cpp

@ -1040,14 +1040,6 @@ u256 StructType::getStorageSize() const
return max<u256>(1, getMembers().getStorageSize()); return max<u256>(1, getMembers().getStorageSize());
} }
bool StructType::canLiveOutsideStorage() const
{
for (auto const& member: getMembers())
if (!member.type->canLiveOutsideStorage())
return false;
return true;
}
string StructType::toString(bool _short) const string StructType::toString(bool _short) const
{ {
string ret = "struct " + m_struct.getName(); string ret = "struct " + m_struct.getName();
@ -1064,9 +1056,13 @@ MemberList const& StructType::getMembers() const
MemberList::MemberMap members; MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers()) for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
{ {
TypePointer type = variable->getType();
// Skip all mapping members if we are not in storage.
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
continue;
members.push_back(MemberList::Member( members.push_back(MemberList::Member(
variable->getName(), variable->getName(),
copyForLocationIfReference(variable->getType()), copyForLocationIfReference(type),
variable.get()) variable.get())
); );
} }
@ -1077,8 +1073,7 @@ MemberList const& StructType::getMembers() const
TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer) const
{ {
auto copy = make_shared<StructType>(m_struct); auto copy = make_shared<StructType>(m_struct, _location);
copy->m_location = _location;
copy->m_isPointer = _isPointer; copy->m_isPointer = _isPointer;
return copy; return copy;
} }
@ -1122,6 +1117,15 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
return 0; return 0;
} }
set<string> StructType::membersMissingInMemory() const
{
set<string> missing;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.getMembers())
if (!variable->getType()->canLiveOutsideStorage())
missing.insert(variable->getName());
return missing;
}
TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const TypePointer EnumType::unaryOperatorResult(Token::Value _operator) const
{ {
return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer(); return _operator == Token::Delete ? make_shared<VoidType>() : TypePointer();

9
libsolidity/Types.h

@ -574,14 +574,14 @@ class StructType: public ReferenceType
{ {
public: public:
virtual Category getCategory() const override { return Category::Struct; } virtual Category getCategory() const override { return Category::Struct; }
explicit StructType(StructDefinition const& _struct): explicit StructType(StructDefinition const& _struct, DataLocation _location = DataLocation::Storage):
ReferenceType(DataLocation::Storage), m_struct(_struct) {} ReferenceType(_location), m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual bool operator==(Type const& _other) const override; virtual bool operator==(Type const& _other) const override;
virtual unsigned getCalldataEncodedSize(bool _padded) const override; virtual unsigned getCalldataEncodedSize(bool _padded) const override;
u256 memorySize() const; u256 memorySize() const;
virtual u256 getStorageSize() const override; virtual u256 getStorageSize() const override;
virtual bool canLiveOutsideStorage() const override; virtual bool canLiveOutsideStorage() const override { return true; }
virtual std::string toString(bool _short) const override; virtual std::string toString(bool _short) const override;
virtual MemberList const& getMembers() const override; virtual MemberList const& getMembers() const override;
@ -597,6 +597,9 @@ public:
StructDefinition const& structDefinition() const { return m_struct; } StructDefinition const& structDefinition() const { return m_struct; }
/// @returns the set of all members that are removed in the memory version (typically mappings).
std::set<std::string> membersMissingInMemory() const;
private: private:
StructDefinition const& m_struct; StructDefinition const& m_struct;
/// List of member types, will be lazy-initialized because of recursive references. /// List of member types, will be lazy-initialized because of recursive references.

23
test/libsolidity/SolidityEndToEndTest.cpp

@ -5034,6 +5034,29 @@ BOOST_AUTO_TEST_CASE(literal_strings)
} }
BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
{
char const* sourceCode = R"(
contract Test {
struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
S s;
function f() returns (uint) {
S memory x;
if (x.a != 0 || x.c != 0) return 1;
x.a = 4; x.c = 5;
s = x;
if (s.a != 4 || s.c != 5) return 2;
x = S(2, 3);
if (x.a != 2 || x.c != 3) return 3;
x = s;
if (s.a != 4 || s.c != 5) return 4;
}
}
)";
compileAndRun(sourceCode, 0, "Test");
BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0)));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

15
test/libsolidity/SolidityNameAndTypeResolution.cpp

@ -2134,6 +2134,21 @@ BOOST_AUTO_TEST_CASE(invalid_integer_literal_exp)
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
} }
BOOST_AUTO_TEST_CASE(memory_structs_with_mappings)
{
char const* text = R"(
contract Test {
struct S { uint8 a; mapping(uint => uint) b; uint8 c; }
S s;
function f() {
S memory x;
x.b[1];
}
}
)";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }

Loading…
Cancel
Save