mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Added containsNestedMapping()
This commit is contained in:
parent
af0cd4ab98
commit
d41eaeba56
@ -112,18 +112,16 @@ bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
|
|||||||
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
||||||
{
|
{
|
||||||
Type const* memberType = member->annotation().type;
|
Type const* memberType = member->annotation().type;
|
||||||
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
|
||||||
{
|
if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||||
if (arrayType->isDynamicallySized())
|
memberType = arrayType->finalBaseType(true);
|
||||||
break;
|
|
||||||
memberType = arrayType->baseType();
|
|
||||||
}
|
|
||||||
if (auto structType = dynamic_cast<StructType const*>(memberType))
|
if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||||
if (_cycleDetector.run(structType->structDefinition()))
|
if (_cycleDetector.run(structType->structDefinition()))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
|
if (util::CycleDetector<StructDefinition>(visitor).run(_struct))
|
||||||
m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
|
m_errorReporter.fatalTypeError(2046_error, _struct.location(), "Recursive struct definition.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -339,31 +339,21 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
|
|||||||
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
|
m_errorReporter.typeError(5587_error, _function.location(), "Internal functions cannot be payable.");
|
||||||
}
|
}
|
||||||
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) {
|
auto checkArgumentAndReturnParameter = [&](VariableDeclaration const& var) {
|
||||||
if (type(var)->category() == Type::Category::Mapping)
|
if (type(var)->containsNestedMapping())
|
||||||
|
if (var.referenceLocation() == VariableDeclaration::Location::Storage)
|
||||||
|
solAssert(
|
||||||
|
_function.libraryFunction() || !_function.isPublic(),
|
||||||
|
"Mapping types for parameters or return variables "
|
||||||
|
"can only be used in internal or library functions."
|
||||||
|
);
|
||||||
|
if (_function.isPublic())
|
||||||
{
|
{
|
||||||
if (var.referenceLocation() != VariableDeclaration::Location::Storage)
|
auto iType = type(var)->interfaceType(_function.libraryFunction());
|
||||||
{
|
|
||||||
if (!_function.libraryFunction() && _function.isPublic())
|
|
||||||
m_errorReporter.typeError(3442_error, var.location(), "Mapping types can only have a data location of \"storage\" and thus only be parameters or return variables for internal or library functions.");
|
|
||||||
else
|
|
||||||
m_errorReporter.typeError(5380_error, var.location(), "Mapping types can only have a data location of \"storage\"." );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
solAssert(_function.libraryFunction() || !_function.isPublic(), "Mapping types for parameters or return variables can only be used in internal or library functions.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!type(var)->canLiveOutsideStorage() && _function.isPublic())
|
|
||||||
m_errorReporter.typeError(3312_error, var.location(), "Type is required to live outside storage.");
|
|
||||||
if (_function.isPublic())
|
|
||||||
{
|
|
||||||
auto iType = type(var)->interfaceType(_function.libraryFunction());
|
|
||||||
|
|
||||||
if (!iType)
|
if (!iType)
|
||||||
{
|
{
|
||||||
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
solAssert(!iType.message().empty(), "Expected detailed error message!");
|
||||||
m_errorReporter.fatalTypeError(4103_error, var.location(), iType.message());
|
m_errorReporter.typeError(4103_error, var.location(), iType.message());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@ -457,9 +447,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
|
m_errorReporter.typeError(1273_error, _variable.location(), "The type of a variable cannot be a library.");
|
||||||
if (_variable.value())
|
if (_variable.value())
|
||||||
{
|
{
|
||||||
if (_variable.isStateVariable() && dynamic_cast<MappingType const*>(varType))
|
if (_variable.isStateVariable() && varType->containsNestedMapping())
|
||||||
{
|
{
|
||||||
m_errorReporter.typeError(6280_error, _variable.location(), "Mappings cannot be assigned to.");
|
m_errorReporter.typeError(
|
||||||
|
6280_error,
|
||||||
|
_variable.location(),
|
||||||
|
"Types in storage containing (nested) mappings cannot be assigned to."
|
||||||
|
);
|
||||||
_variable.value()->accept(*this);
|
_variable.value()->accept(*this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -499,9 +493,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
|
|
||||||
if (!_variable.isStateVariable())
|
if (!_variable.isStateVariable())
|
||||||
{
|
{
|
||||||
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
|
if (
|
||||||
if (!varType->canLiveOutsideStorage())
|
_variable.referenceLocation() == VariableDeclaration::Location::CallData ||
|
||||||
m_errorReporter.typeError(4061_error, _variable.location(), "Type " + varType->toString() + " is only valid in storage.");
|
_variable.referenceLocation() == VariableDeclaration::Location::Memory
|
||||||
|
)
|
||||||
|
if (varType->containsNestedMapping())
|
||||||
|
m_errorReporter.fatalTypeError(
|
||||||
|
4061_error,
|
||||||
|
_variable.location(),
|
||||||
|
"Type " + varType->toString(true) + " is only valid in storage because it contains a (nested) mapping."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else if (_variable.visibility() >= Visibility::Public)
|
else if (_variable.visibility() >= Visibility::Public)
|
||||||
{
|
{
|
||||||
@ -612,8 +613,12 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|||||||
{
|
{
|
||||||
if (var->isIndexed())
|
if (var->isIndexed())
|
||||||
numIndexed++;
|
numIndexed++;
|
||||||
if (!type(*var)->canLiveOutsideStorage())
|
if (type(*var)->containsNestedMapping())
|
||||||
m_errorReporter.typeError(3448_error, var->location(), "Type is required to live outside storage.");
|
m_errorReporter.typeError(
|
||||||
|
3448_error,
|
||||||
|
var->location(),
|
||||||
|
"Type containing a (nested) mapping is not allowed as event parameter type."
|
||||||
|
);
|
||||||
if (!type(*var)->interfaceType(false))
|
if (!type(*var)->interfaceType(false))
|
||||||
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
|
||||||
if (
|
if (
|
||||||
@ -1386,7 +1391,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
|
|||||||
checkExpressionAssignment(*types[i], *tupleExpression->components()[i]);
|
checkExpressionAssignment(*types[i], *tupleExpression->components()[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_type.category() == Type::Category::Mapping)
|
else if (_type.nameable() && _type.containsNestedMapping())
|
||||||
{
|
{
|
||||||
bool isLocalOrReturn = false;
|
bool isLocalOrReturn = false;
|
||||||
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
|
if (auto const* identifier = dynamic_cast<Identifier const*>(&_expression))
|
||||||
@ -1394,7 +1399,7 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const&
|
|||||||
if (variableDeclaration->isLocalOrReturn())
|
if (variableDeclaration->isLocalOrReturn())
|
||||||
isLocalOrReturn = true;
|
isLocalOrReturn = true;
|
||||||
if (!isLocalOrReturn)
|
if (!isLocalOrReturn)
|
||||||
m_errorReporter.typeError(9214_error, _expression.location(), "Mappings cannot be assigned to.");
|
m_errorReporter.typeError(9214_error, _expression.location(), "Types in storage containing (nested) mappings cannot be assigned to.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1530,8 +1535,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
|
|||||||
_tuple.location(),
|
_tuple.location(),
|
||||||
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
|
"Unable to deduce nameable type for array elements. Try adding explicit type conversion for the first element."
|
||||||
);
|
);
|
||||||
else if (!inlineArrayType->canLiveOutsideStorage())
|
else if (inlineArrayType->containsNestedMapping())
|
||||||
m_errorReporter.fatalTypeError(1545_error, _tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage.");
|
m_errorReporter.fatalTypeError(
|
||||||
|
1545_error,
|
||||||
|
_tuple.location(),
|
||||||
|
"Type " + inlineArrayType->toString(true) + " is only valid in storage."
|
||||||
|
);
|
||||||
|
|
||||||
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
|
_tuple.annotation().type = TypeProvider::array(DataLocation::Memory, inlineArrayType, types.size());
|
||||||
}
|
}
|
||||||
@ -2032,24 +2041,8 @@ void TypeChecker::typeCheckFunctionGeneralChecks(
|
|||||||
toString(parameterTypes.size()) +
|
toString(parameterTypes.size()) +
|
||||||
".";
|
".";
|
||||||
|
|
||||||
// Extend error message in case we try to construct a struct with mapping member.
|
|
||||||
if (isStructConstructorCall)
|
if (isStructConstructorCall)
|
||||||
{
|
|
||||||
/// For error message: Struct members that were removed during conversion to memory.
|
|
||||||
TypePointer const expressionType = type(_functionCall.expression());
|
|
||||||
auto const& t = dynamic_cast<TypeType const&>(*expressionType);
|
|
||||||
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
|
||||||
set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
|
||||||
|
|
||||||
if (!membersRemovedForStructConstructor.empty())
|
|
||||||
{
|
|
||||||
msg += " Members that have to be skipped in memory:";
|
|
||||||
for (auto const& member: membersRemovedForStructConstructor)
|
|
||||||
msg += " " + member;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { isVariadic ? 1123_error : 9755_error, msg };
|
return { isVariadic ? 1123_error : 9755_error, msg };
|
||||||
}
|
|
||||||
else if (
|
else if (
|
||||||
_functionType->kind() == FunctionType::Kind::BareCall ||
|
_functionType->kind() == FunctionType::Kind::BareCall ||
|
||||||
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
_functionType->kind() == FunctionType::Kind::BareCallCode ||
|
||||||
@ -2269,6 +2262,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|||||||
|
|
||||||
if (actualType->category() == Type::Category::Struct)
|
if (actualType->category() == Type::Category::Struct)
|
||||||
{
|
{
|
||||||
|
if (actualType->containsNestedMapping())
|
||||||
|
m_errorReporter.fatalTypeError(
|
||||||
|
9515_error,
|
||||||
|
_functionCall.location(),
|
||||||
|
"Struct containing a (nested) mapping cannot be constructed."
|
||||||
|
);
|
||||||
functionType = dynamic_cast<StructType const&>(*actualType).constructorType();
|
functionType = dynamic_cast<StructType const&>(*actualType).constructorType();
|
||||||
funcCallAnno.kind = FunctionCallKind::StructConstructorCall;
|
funcCallAnno.kind = FunctionCallKind::StructConstructorCall;
|
||||||
funcCallAnno.isPure = argumentsArePure;
|
funcCallAnno.isPure = argumentsArePure;
|
||||||
@ -2518,11 +2517,11 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|||||||
}
|
}
|
||||||
else if (type->category() == Type::Category::Array)
|
else if (type->category() == Type::Category::Array)
|
||||||
{
|
{
|
||||||
if (!type->canLiveOutsideStorage())
|
if (type->containsNestedMapping())
|
||||||
m_errorReporter.fatalTypeError(
|
m_errorReporter.fatalTypeError(
|
||||||
1164_error,
|
1164_error,
|
||||||
_newExpression.typeName().location(),
|
_newExpression.typeName().location(),
|
||||||
"Type cannot live outside storage."
|
"Array containing a (nested) mapping cannot be constructed in memory."
|
||||||
);
|
);
|
||||||
if (!type->isDynamicallySized())
|
if (!type->isDynamicallySized())
|
||||||
m_errorReporter.typeError(
|
m_errorReporter.typeError(
|
||||||
|
@ -136,6 +136,9 @@ struct StructDeclarationAnnotation: TypeDeclarationAnnotation
|
|||||||
/// recursion immediately raises an error.
|
/// recursion immediately raises an error.
|
||||||
/// Will be filled in by the DeclarationTypeChecker.
|
/// Will be filled in by the DeclarationTypeChecker.
|
||||||
std::optional<bool> recursive;
|
std::optional<bool> recursive;
|
||||||
|
/// Whether the struct contains a mapping type, either directly or, indirectly inside another
|
||||||
|
/// struct or an array.
|
||||||
|
std::optional<bool> containsNestedMapping;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
|
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
|
||||||
|
@ -1985,6 +1985,20 @@ TypeResult ArrayType::interfaceType(bool _inLibrary) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type const* ArrayType::finalBaseType(bool _breakIfDynamicArrayType) const
|
||||||
|
{
|
||||||
|
Type const* finalBaseType = this;
|
||||||
|
|
||||||
|
while (auto arrayType = dynamic_cast<ArrayType const*>(finalBaseType))
|
||||||
|
{
|
||||||
|
if (_breakIfDynamicArrayType && arrayType->isDynamicallySized())
|
||||||
|
break;
|
||||||
|
finalBaseType = arrayType->baseType();
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalBaseType;
|
||||||
|
}
|
||||||
|
|
||||||
u256 ArrayType::memoryDataSize() const
|
u256 ArrayType::memoryDataSize() const
|
||||||
{
|
{
|
||||||
solAssert(!isDynamicallySized(), "");
|
solAssert(!isDynamicallySized(), "");
|
||||||
@ -2213,7 +2227,7 @@ unsigned StructType::calldataEncodedSize(bool) const
|
|||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& member: members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
// Struct members are always padded.
|
// Struct members are always padded.
|
||||||
size += member.type->calldataEncodedSize();
|
size += member.type->calldataEncodedSize();
|
||||||
}
|
}
|
||||||
@ -2228,7 +2242,7 @@ unsigned StructType::calldataEncodedTailSize() const
|
|||||||
unsigned size = 0;
|
unsigned size = 0;
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& member: members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
// Struct members are always padded.
|
// Struct members are always padded.
|
||||||
size += member.type->calldataHeadSize();
|
size += member.type->calldataHeadSize();
|
||||||
}
|
}
|
||||||
@ -2240,7 +2254,7 @@ unsigned StructType::calldataOffsetOfMember(std::string const& _member) const
|
|||||||
unsigned offset = 0;
|
unsigned offset = 0;
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& member: members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
if (member.name == _member)
|
if (member.name == _member)
|
||||||
return offset;
|
return offset;
|
||||||
// Struct members are always padded.
|
// Struct members are always padded.
|
||||||
@ -2277,6 +2291,42 @@ u256 StructType::storageSize() const
|
|||||||
return max<u256>(1, members(nullptr).storageSize());
|
return max<u256>(1, members(nullptr).storageSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool StructType::containsNestedMapping() const
|
||||||
|
{
|
||||||
|
if (!m_struct.annotation().containsNestedMapping.has_value())
|
||||||
|
{
|
||||||
|
bool hasNestedMapping = false;
|
||||||
|
|
||||||
|
util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}};
|
||||||
|
|
||||||
|
breadthFirstSearch.run(
|
||||||
|
[&](StructDefinition const* _struct, auto&& _addChild)
|
||||||
|
{
|
||||||
|
for (auto const& member: _struct->members())
|
||||||
|
{
|
||||||
|
TypePointer memberType = member->annotation().type;
|
||||||
|
solAssert(memberType, "");
|
||||||
|
|
||||||
|
if (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
|
||||||
|
memberType = arrayType->finalBaseType(false);
|
||||||
|
|
||||||
|
if (dynamic_cast<MappingType const*>(memberType))
|
||||||
|
{
|
||||||
|
hasNestedMapping = true;
|
||||||
|
breadthFirstSearch.abort();
|
||||||
|
}
|
||||||
|
else if (auto structType = dynamic_cast<StructType const*>(memberType))
|
||||||
|
_addChild(&structType->structDefinition());
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
m_struct.annotation().containsNestedMapping = hasNestedMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_struct.annotation().containsNestedMapping.value();
|
||||||
|
}
|
||||||
|
|
||||||
string StructType::toString(bool _short) const
|
string StructType::toString(bool _short) const
|
||||||
{
|
{
|
||||||
string ret = "struct " + m_struct.annotation().canonicalName;
|
string ret = "struct " + m_struct.annotation().canonicalName;
|
||||||
@ -2292,10 +2342,7 @@ MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
|
|||||||
{
|
{
|
||||||
TypePointer type = variable->annotation().type;
|
TypePointer type = variable->annotation().type;
|
||||||
solAssert(type, "");
|
solAssert(type, "");
|
||||||
// If we are not in storage, skip all members that cannot live outside of storage,
|
solAssert(!(location() != DataLocation::Storage && type->containsNestedMapping()), "");
|
||||||
// ex. mappings and array of mappings
|
|
||||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
|
||||||
continue;
|
|
||||||
members.emplace_back(
|
members.emplace_back(
|
||||||
variable->name(),
|
variable->name(),
|
||||||
copyForLocationIfReference(type),
|
copyForLocationIfReference(type),
|
||||||
@ -2457,10 +2504,9 @@ FunctionTypePointer StructType::constructorType() const
|
|||||||
{
|
{
|
||||||
TypePointers paramTypes;
|
TypePointers paramTypes;
|
||||||
strings paramNames;
|
strings paramNames;
|
||||||
|
solAssert(!containsNestedMapping(), "");
|
||||||
for (auto const& member: members(nullptr))
|
for (auto const& member: members(nullptr))
|
||||||
{
|
{
|
||||||
if (!member.type->canLiveOutsideStorage())
|
|
||||||
continue;
|
|
||||||
paramNames.push_back(member.name);
|
paramNames.push_back(member.name);
|
||||||
paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type));
|
paramTypes.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, member.type));
|
||||||
}
|
}
|
||||||
@ -2494,20 +2540,12 @@ u256 StructType::memoryOffsetOfMember(string const& _name) const
|
|||||||
|
|
||||||
TypePointers StructType::memoryMemberTypes() const
|
TypePointers StructType::memoryMemberTypes() const
|
||||||
{
|
{
|
||||||
|
solAssert(!containsNestedMapping(), "");
|
||||||
TypePointers types;
|
TypePointers types;
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||||
if (variable->annotation().type->canLiveOutsideStorage())
|
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
|
||||||
types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type));
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
set<string> StructType::membersMissingInMemory() const
|
return types;
|
||||||
{
|
|
||||||
set<string> missing;
|
|
||||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
|
||||||
if (!variable->annotation().type->canLiveOutsideStorage())
|
|
||||||
missing.insert(variable->name());
|
|
||||||
return missing;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<tuple<string, TypePointer>> StructType::makeStackItems() const
|
vector<tuple<string, TypePointer>> StructType::makeStackItems() const
|
||||||
@ -3654,7 +3692,10 @@ TypeResult MappingType::interfaceType(bool _inLibrary) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return TypeResult::err("Only libraries are allowed to use the mapping type in public or external functions.");
|
return TypeResult::err(
|
||||||
|
"Types containing (nested) mappings can only be parameters or "
|
||||||
|
"return variables of internal or library functions."
|
||||||
|
);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,11 @@ public:
|
|||||||
/// Returns true if the type can be stored in storage.
|
/// Returns true if the type can be stored in storage.
|
||||||
virtual bool canBeStored() const { return true; }
|
virtual bool canBeStored() const { return true; }
|
||||||
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
/// Returns false if the type cannot live outside the storage, i.e. if it includes some mapping.
|
||||||
virtual bool canLiveOutsideStorage() const { return true; }
|
virtual bool containsNestedMapping() const
|
||||||
|
{
|
||||||
|
solAssert(nameable(), "Called for a non nameable type.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
/// Returns true if the type can be stored as a value (as opposed to a reference) on the stack,
|
||||||
/// i.e. it behaves differently in lvalue context and in value context.
|
/// i.e. it behaves differently in lvalue context and in value context.
|
||||||
virtual bool isValueType() const { return false; }
|
virtual bool isValueType() const { return false; }
|
||||||
@ -549,7 +553,6 @@ public:
|
|||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
u256 literalValue(Literal const* _literal) const override;
|
u256 literalValue(Literal const* _literal) const override;
|
||||||
@ -610,7 +613,6 @@ public:
|
|||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
|
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
|
|
||||||
std::string toString(bool) const override;
|
std::string toString(bool) const override;
|
||||||
TypePointer mobileType() const override;
|
TypePointer mobileType() const override;
|
||||||
@ -781,8 +783,9 @@ public:
|
|||||||
bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
bool isDynamicallySized() const override { return m_hasDynamicLength; }
|
||||||
bool isDynamicallyEncoded() const override;
|
bool isDynamicallyEncoded() const override;
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return m_baseType->canLiveOutsideStorage(); }
|
bool containsNestedMapping() const override { return m_baseType->containsNestedMapping(); }
|
||||||
bool nameable() const override { return true; }
|
bool nameable() const override { return true; }
|
||||||
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
std::string signatureInExternalFunction(bool _structsByName) const override;
|
std::string signatureInExternalFunction(bool _structsByName) const override;
|
||||||
@ -798,6 +801,7 @@ public:
|
|||||||
/// @returns true if this is a string
|
/// @returns true if this is a string
|
||||||
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
bool isString() const { return m_arrayKind == ArrayKind::String; }
|
||||||
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
Type const* baseType() const { solAssert(!!m_baseType, ""); return m_baseType; }
|
||||||
|
Type const* finalBaseType(bool breakIfDynamicArrayType) const;
|
||||||
u256 const& length() const { return m_length; }
|
u256 const& length() const { return m_length; }
|
||||||
u256 memoryDataSize() const override;
|
u256 memoryDataSize() const override;
|
||||||
|
|
||||||
@ -842,7 +846,6 @@ public:
|
|||||||
unsigned calldataEncodedTailSize() const override { return 32; }
|
unsigned calldataEncodedTailSize() const override { return 32; }
|
||||||
bool isDynamicallySized() const override { return true; }
|
bool isDynamicallySized() const override { return true; }
|
||||||
bool isDynamicallyEncoded() const override { return true; }
|
bool isDynamicallyEncoded() const override { return true; }
|
||||||
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
TypePointer mobileType() const override;
|
TypePointer mobileType() const override;
|
||||||
|
|
||||||
@ -883,7 +886,6 @@ public:
|
|||||||
}
|
}
|
||||||
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
|
unsigned storageBytes() const override { solAssert(!isSuper(), ""); return 20; }
|
||||||
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
|
bool leftAligned() const override { solAssert(!isSuper(), ""); return false; }
|
||||||
bool canLiveOutsideStorage() const override { return !isSuper(); }
|
|
||||||
bool isValueType() const override { return !isSuper(); }
|
bool isValueType() const override { return !isSuper(); }
|
||||||
bool nameable() const override { return !isSuper(); }
|
bool nameable() const override { return !isSuper(); }
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
@ -945,7 +947,7 @@ public:
|
|||||||
bool isDynamicallyEncoded() const override;
|
bool isDynamicallyEncoded() const override;
|
||||||
u256 memoryDataSize() const override;
|
u256 memoryDataSize() const override;
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return true; }
|
bool containsNestedMapping() const override;
|
||||||
bool nameable() const override { return true; }
|
bool nameable() const override { return true; }
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
|
|
||||||
@ -975,8 +977,6 @@ public:
|
|||||||
|
|
||||||
/// @returns the vector of types of members available in memory.
|
/// @returns the vector of types of members available in memory.
|
||||||
TypePointers memoryMemberTypes() const;
|
TypePointers memoryMemberTypes() const;
|
||||||
/// @returns the set of all members that are removed in the memory version (typically mappings).
|
|
||||||
std::set<std::string> membersMissingInMemory() const;
|
|
||||||
|
|
||||||
void clearCache() const override;
|
void clearCache() const override;
|
||||||
|
|
||||||
@ -1007,7 +1007,6 @@ public:
|
|||||||
}
|
}
|
||||||
unsigned storageBytes() const override;
|
unsigned storageBytes() const override;
|
||||||
bool leftAligned() const override { return false; }
|
bool leftAligned() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return true; }
|
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
bool isValueType() const override { return true; }
|
bool isValueType() const override { return true; }
|
||||||
@ -1047,7 +1046,6 @@ public:
|
|||||||
std::string toString(bool) const override;
|
std::string toString(bool) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { return false; }
|
bool hasSimpleZeroValueInMemory() const override { return false; }
|
||||||
TypePointer mobileType() const override;
|
TypePointer mobileType() const override;
|
||||||
/// Converts components to their temporary types and performs some wildcard matching.
|
/// Converts components to their temporary types and performs some wildcard matching.
|
||||||
@ -1218,7 +1216,6 @@ public:
|
|||||||
unsigned storageBytes() const override;
|
unsigned storageBytes() const override;
|
||||||
bool isValueType() const override { return true; }
|
bool isValueType() const override { return true; }
|
||||||
bool nameable() const override;
|
bool nameable() const override;
|
||||||
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { return false; }
|
bool hasSimpleZeroValueInMemory() const override { return false; }
|
||||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||||
TypePointer encodingType() const override;
|
TypePointer encodingType() const override;
|
||||||
@ -1351,7 +1348,7 @@ public:
|
|||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
std::string toString(bool _short) const override;
|
std::string toString(bool _short) const override;
|
||||||
std::string canonicalName() const override;
|
std::string canonicalName() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
bool containsNestedMapping() const override { return true; }
|
||||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||||
Type const* encodingType() const override;
|
Type const* encodingType() const override;
|
||||||
TypeResult interfaceType(bool _inLibrary) const override;
|
TypeResult interfaceType(bool _inLibrary) const override;
|
||||||
@ -1386,7 +1383,6 @@ public:
|
|||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||||
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
|
||||||
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
|
||||||
@ -1412,7 +1408,6 @@ public:
|
|||||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
u256 storageSize() const override;
|
u256 storageSize() const override;
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
@ -1439,7 +1434,6 @@ public:
|
|||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return true; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||||
|
|
||||||
@ -1479,7 +1473,6 @@ public:
|
|||||||
std::string richIdentifier() const override;
|
std::string richIdentifier() const override;
|
||||||
bool operator==(Type const& _other) const override;
|
bool operator==(Type const& _other) const override;
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return true; }
|
|
||||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||||
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
|
||||||
|
|
||||||
@ -1512,7 +1505,6 @@ public:
|
|||||||
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
TypeResult binaryOperatorResult(Token, Type const*) const override { return nullptr; }
|
||||||
unsigned calldataEncodedSize(bool) const override { return 32; }
|
unsigned calldataEncodedSize(bool) const override { return 32; }
|
||||||
bool canBeStored() const override { return false; }
|
bool canBeStored() const override { return false; }
|
||||||
bool canLiveOutsideStorage() const override { return false; }
|
|
||||||
bool isValueType() const override { return true; }
|
bool isValueType() const override { return true; }
|
||||||
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
|
||||||
std::string toString(bool) const override { return "inaccessible dynamic type"; }
|
std::string toString(bool) const override { return "inaccessible dynamic type"; }
|
||||||
|
@ -860,8 +860,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
|
|||||||
for (auto const& member: _to.members(nullptr))
|
for (auto const& member: _to.members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(member.type, "");
|
solAssert(member.type, "");
|
||||||
if (!member.type->canLiveOutsideStorage())
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
continue;
|
|
||||||
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
|
TypePointer memberTypeTo = member.type->fullEncodingType(_options.encodeAsLibraryTypes, true, false);
|
||||||
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
|
solUnimplementedAssert(memberTypeTo, "Encoding type \"" + member.type->toString() + "\" not yet implemented.");
|
||||||
auto memberTypeFrom = _from.memberType(member.name);
|
auto memberTypeFrom = _from.memberType(member.name);
|
||||||
@ -1340,7 +1339,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
|
|||||||
for (auto const& member: _type.members(nullptr))
|
for (auto const& member: _type.members(nullptr))
|
||||||
{
|
{
|
||||||
solAssert(member.type, "");
|
solAssert(member.type, "");
|
||||||
solAssert(member.type->canLiveOutsideStorage(), "");
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
auto decodingType = member.type->decodingType();
|
auto decodingType = member.type->decodingType();
|
||||||
solAssert(decodingType, "");
|
solAssert(decodingType, "");
|
||||||
bool dynamic = decodingType->isDynamicallyEncoded();
|
bool dynamic = decodingType->isDynamicallyEncoded();
|
||||||
|
@ -1069,8 +1069,7 @@ void CompilerUtils::convertType(
|
|||||||
// stack: <memory ptr> <source ref> <memory ptr>
|
// stack: <memory ptr> <source ref> <memory ptr>
|
||||||
for (auto const& member: typeOnStack->members(nullptr))
|
for (auto const& member: typeOnStack->members(nullptr))
|
||||||
{
|
{
|
||||||
if (!member.type->canLiveOutsideStorage())
|
solAssert(!member.type->containsNestedMapping(), "");
|
||||||
continue;
|
|
||||||
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name);
|
||||||
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
_context << offsets.first << Instruction::DUP3 << Instruction::ADD;
|
||||||
_context << u256(offsets.second);
|
_context << u256(offsets.second);
|
||||||
|
@ -355,13 +355,13 @@ void StorageItem::storeValue(Type const& _sourceType, SourceLocation const& _loc
|
|||||||
structType.structDefinition() == sourceType.structDefinition(),
|
structType.structDefinition() == sourceType.structDefinition(),
|
||||||
"Struct assignment with conversion."
|
"Struct assignment with conversion."
|
||||||
);
|
);
|
||||||
|
solAssert(!structType.containsNestedMapping(), "");
|
||||||
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
solAssert(sourceType.location() != DataLocation::CallData, "Structs in calldata not supported.");
|
||||||
for (auto const& member: structType.members(nullptr))
|
for (auto const& member: structType.members(nullptr))
|
||||||
{
|
{
|
||||||
// assign each member that can live outside of storage
|
// assign each member that can live outside of storage
|
||||||
TypePointer const& memberType = member.type;
|
TypePointer const& memberType = member.type;
|
||||||
if (!memberType->canLiveOutsideStorage())
|
solAssert(memberType->nameable(), "");
|
||||||
continue;
|
|
||||||
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
TypePointer sourceMemberType = sourceType.memberType(member.name);
|
||||||
if (sourceType.location() == DataLocation::Storage)
|
if (sourceType.location() == DataLocation::Storage)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user