mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Struct constructors.
This commit is contained in:
parent
6059d20750
commit
e2d6e34f9c
168
AST.cpp
168
AST.cpp
@ -802,12 +802,11 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
|
|||||||
|
|
||||||
m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr);
|
m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr);
|
||||||
|
|
||||||
Type const* expressionType = m_expression->getType().get();
|
TypePointer const& expressionType = m_expression->getType();
|
||||||
|
FunctionTypePointer functionType;
|
||||||
if (isTypeConversion())
|
if (isTypeConversion())
|
||||||
{
|
{
|
||||||
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
|
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
|
||||||
//@todo for structs, we have to check the number of arguments to be equal to the
|
|
||||||
// number of non-mapping members
|
|
||||||
if (m_arguments.size() != 1)
|
if (m_arguments.size() != 1)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion."));
|
BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion."));
|
||||||
if (!isPositionalCall)
|
if (!isPositionalCall)
|
||||||
@ -815,87 +814,106 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
|
|||||||
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
|
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
|
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
|
||||||
m_type = type.getActualType();
|
m_type = type.getActualType();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (FunctionType const* functionType = dynamic_cast<FunctionType const*>(expressionType))
|
|
||||||
|
if (isStructConstructorCall())
|
||||||
{
|
{
|
||||||
//@todo would be nice to create a struct type from the arguments
|
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
|
||||||
// and then ask if that is implicitly convertible to the struct represented by the
|
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
|
||||||
// function parameters
|
functionType = structType.constructorType();
|
||||||
TypePointers const& parameterTypes = functionType->getParameterTypes();
|
|
||||||
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
|
|
||||||
|
|
||||||
if (isPositionalCall)
|
|
||||||
{
|
|
||||||
// call by positional arguments
|
|
||||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
|
||||||
if (
|
|
||||||
!functionType->takesArbitraryParameters() &&
|
|
||||||
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])
|
|
||||||
)
|
|
||||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
|
|
||||||
"Invalid type for argument in function call. "
|
|
||||||
"Invalid implicit conversion from " +
|
|
||||||
m_arguments[i]->getType()->toString() +
|
|
||||||
" to " +
|
|
||||||
parameterTypes[i]->toString() +
|
|
||||||
" requested."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// call by named arguments
|
|
||||||
if (functionType->takesArbitraryParameters())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions "
|
|
||||||
"that take arbitrary parameters."));
|
|
||||||
auto const& parameterNames = functionType->getParameterNames();
|
|
||||||
if (parameterNames.size() != m_names.size())
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
|
|
||||||
|
|
||||||
// check duplicate names
|
|
||||||
for (size_t i = 0; i < m_names.size(); i++)
|
|
||||||
for (size_t j = i + 1; j < m_names.size(); j++)
|
|
||||||
if (*m_names[i] == *m_names[j])
|
|
||||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < m_names.size(); i++) {
|
|
||||||
bool found = false;
|
|
||||||
for (size_t j = 0; j < parameterNames.size(); j++) {
|
|
||||||
if (parameterNames[j] == *m_names[i]) {
|
|
||||||
// check type convertible
|
|
||||||
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
|
|
||||||
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
|
|
||||||
"Invalid type for argument in function call. "
|
|
||||||
"Invalid implicit conversion from " +
|
|
||||||
m_arguments[i]->getType()->toString() +
|
|
||||||
" to " +
|
|
||||||
parameterTypes[i]->toString() +
|
|
||||||
" requested."
|
|
||||||
));
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @todo actually the return type should be an anonymous struct,
|
|
||||||
// but we change it to the type of the first return value until we have structs
|
|
||||||
if (functionType->getReturnParameterTypes().empty())
|
|
||||||
m_type = make_shared<VoidType>();
|
|
||||||
else
|
|
||||||
m_type = functionType->getReturnParameterTypes().front();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
|
||||||
|
|
||||||
|
if (!functionType)
|
||||||
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
|
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable."));
|
||||||
|
|
||||||
|
//@todo would be nice to create a struct type from the arguments
|
||||||
|
// and then ask if that is implicitly convertible to the struct represented by the
|
||||||
|
// function parameters
|
||||||
|
TypePointers const& parameterTypes = functionType->getParameterTypes();
|
||||||
|
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call."));
|
||||||
|
|
||||||
|
if (isPositionalCall)
|
||||||
|
{
|
||||||
|
// call by positional arguments
|
||||||
|
for (size_t i = 0; i < m_arguments.size(); ++i)
|
||||||
|
if (
|
||||||
|
!functionType->takesArbitraryParameters() &&
|
||||||
|
!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[i])
|
||||||
|
)
|
||||||
|
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
|
||||||
|
"Invalid type for argument in function call. "
|
||||||
|
"Invalid implicit conversion from " +
|
||||||
|
m_arguments[i]->getType()->toString() +
|
||||||
|
" to " +
|
||||||
|
parameterTypes[i]->toString() +
|
||||||
|
" requested."
|
||||||
|
));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// call by named arguments
|
||||||
|
if (functionType->takesArbitraryParameters())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError(
|
||||||
|
"Named arguments cannnot be used for functions that take arbitrary parameters."
|
||||||
|
));
|
||||||
|
auto const& parameterNames = functionType->getParameterNames();
|
||||||
|
if (parameterNames.size() != m_names.size())
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Some argument names are missing."));
|
||||||
|
|
||||||
|
// check duplicate names
|
||||||
|
for (size_t i = 0; i < m_names.size(); i++)
|
||||||
|
for (size_t j = i + 1; j < m_names.size(); j++)
|
||||||
|
if (*m_names[i] == *m_names[j])
|
||||||
|
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError("Duplicate named argument."));
|
||||||
|
|
||||||
|
for (size_t i = 0; i < m_names.size(); i++) {
|
||||||
|
bool found = false;
|
||||||
|
for (size_t j = 0; j < parameterNames.size(); j++) {
|
||||||
|
if (parameterNames[j] == *m_names[i]) {
|
||||||
|
// check type convertible
|
||||||
|
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameterTypes[j]))
|
||||||
|
BOOST_THROW_EXCEPTION(m_arguments[i]->createTypeError(
|
||||||
|
"Invalid type for argument in function call. "
|
||||||
|
"Invalid implicit conversion from " +
|
||||||
|
m_arguments[i]->getType()->toString() +
|
||||||
|
" to " +
|
||||||
|
parameterTypes[i]->toString() +
|
||||||
|
" requested."
|
||||||
|
));
|
||||||
|
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found)
|
||||||
|
BOOST_THROW_EXCEPTION(createTypeError("Named argument does not match function declaration."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @todo actually the return type should be an anonymous struct,
|
||||||
|
// but we change it to the type of the first return value until we have anonymous
|
||||||
|
// structs and tuples
|
||||||
|
if (functionType->getReturnParameterTypes().empty())
|
||||||
|
m_type = make_shared<VoidType>();
|
||||||
|
else
|
||||||
|
m_type = functionType->getReturnParameterTypes().front();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FunctionCall::isTypeConversion() const
|
bool FunctionCall::isTypeConversion() const
|
||||||
{
|
{
|
||||||
return m_expression->getType()->getCategory() == Type::Category::TypeType;
|
return m_expression->getType()->getCategory() == Type::Category::TypeType && !isStructConstructorCall();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FunctionCall::isStructConstructorCall() const
|
||||||
|
{
|
||||||
|
if (auto const* type = dynamic_cast<TypeType const*>(m_expression->getType().get()))
|
||||||
|
return type->getActualType()->getCategory() == Type::Category::Struct;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NewExpression::checkTypeRequirements(TypePointers const*)
|
void NewExpression::checkTypeRequirements(TypePointers const*)
|
||||||
|
6
AST.h
6
AST.h
@ -1136,9 +1136,11 @@ public:
|
|||||||
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
std::vector<ASTPointer<Expression const>> getArguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
||||||
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
|
std::vector<ASTPointer<ASTString>> const& getNames() const { return m_names; }
|
||||||
|
|
||||||
/// Returns true if this is not an actual function call, but an explicit type conversion
|
/// @returns true if this is not an actual function call, but an explicit type conversion.
|
||||||
/// or constructor call.
|
/// Returns false for struct constructor calls.
|
||||||
bool isTypeConversion() const;
|
bool isTypeConversion() const;
|
||||||
|
/// @return true if this is a constructor call for a struct, i.e. StructName(...).
|
||||||
|
bool isStructConstructorCall() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ASTPointer<Expression> m_expression;
|
ASTPointer<Expression> m_expression;
|
||||||
|
@ -313,38 +313,66 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
using Location = FunctionType::Location;
|
using Location = FunctionType::Location;
|
||||||
if (_functionCall.isTypeConversion())
|
if (_functionCall.isTypeConversion())
|
||||||
{
|
{
|
||||||
//@todo struct construction
|
|
||||||
solAssert(_functionCall.getArguments().size() == 1, "");
|
solAssert(_functionCall.getArguments().size() == 1, "");
|
||||||
solAssert(_functionCall.getNames().empty(), "");
|
solAssert(_functionCall.getNames().empty(), "");
|
||||||
Expression const& firstArgument = *_functionCall.getArguments().front();
|
Expression const& firstArgument = *_functionCall.getArguments().front();
|
||||||
firstArgument.accept(*this);
|
firstArgument.accept(*this);
|
||||||
utils().convertType(*firstArgument.getType(), *_functionCall.getType());
|
utils().convertType(*firstArgument.getType(), *_functionCall.getType());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionTypePointer functionType;
|
||||||
|
if (_functionCall.isStructConstructorCall())
|
||||||
|
{
|
||||||
|
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.getExpression().getType());
|
||||||
|
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
|
||||||
|
functionType = structType.constructorType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.getExpression().getType());
|
||||||
|
|
||||||
|
TypePointers const& parameterTypes = functionType->getParameterTypes();
|
||||||
|
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
|
||||||
|
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
|
||||||
|
if (!functionType->takesArbitraryParameters())
|
||||||
|
solAssert(callArguments.size() == parameterTypes.size(), "");
|
||||||
|
|
||||||
|
vector<ASTPointer<Expression const>> arguments;
|
||||||
|
if (callArgumentNames.empty())
|
||||||
|
// normal arguments
|
||||||
|
arguments = callArguments;
|
||||||
|
else
|
||||||
|
// named arguments
|
||||||
|
for (auto const& parameterName: functionType->getParameterNames())
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
|
||||||
|
if ((found = (parameterName == *callArgumentNames[j])))
|
||||||
|
// we found the actual parameter position
|
||||||
|
arguments.push_back(callArguments[j]);
|
||||||
|
solAssert(found, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_functionCall.isStructConstructorCall())
|
||||||
|
{
|
||||||
|
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.getExpression().getType());
|
||||||
|
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
|
||||||
|
|
||||||
|
m_context << u256(max(32u, structType.getCalldataEncodedSize(true)));
|
||||||
|
utils().allocateMemory();
|
||||||
|
m_context << eth::Instruction::DUP1;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||||
|
{
|
||||||
|
arguments[i]->accept(*this);
|
||||||
|
utils().convertType(*arguments[i]->getType(), *functionType->getParameterTypes()[i]);
|
||||||
|
utils().storeInMemoryDynamic(*functionType->getParameterTypes()[i]);
|
||||||
|
}
|
||||||
|
m_context << eth::Instruction::POP;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
|
FunctionType const& function = *functionType;
|
||||||
TypePointers const& parameterTypes = function.getParameterTypes();
|
|
||||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.getArguments();
|
|
||||||
vector<ASTPointer<ASTString>> const& callArgumentNames = _functionCall.getNames();
|
|
||||||
if (!function.takesArbitraryParameters())
|
|
||||||
solAssert(callArguments.size() == parameterTypes.size(), "");
|
|
||||||
|
|
||||||
vector<ASTPointer<Expression const>> arguments;
|
|
||||||
if (callArgumentNames.empty())
|
|
||||||
// normal arguments
|
|
||||||
arguments = callArguments;
|
|
||||||
else
|
|
||||||
// named arguments
|
|
||||||
for (auto const& parameterName: function.getParameterNames())
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
for (size_t j = 0; j < callArgumentNames.size() && !found; j++)
|
|
||||||
if ((found = (parameterName == *callArgumentNames[j])))
|
|
||||||
// we found the actual parameter position
|
|
||||||
arguments.push_back(callArguments[j]);
|
|
||||||
solAssert(found, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (function.getLocation())
|
switch (function.getLocation())
|
||||||
{
|
{
|
||||||
case Location::Internal:
|
case Location::Internal:
|
||||||
|
30
Types.cpp
30
Types.cpp
@ -30,11 +30,8 @@
|
|||||||
#include <libsolidity/AST.h>
|
#include <libsolidity/AST.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace dev;
|
||||||
namespace dev
|
using namespace dev::solidity;
|
||||||
{
|
|
||||||
namespace solidity
|
|
||||||
{
|
|
||||||
|
|
||||||
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
||||||
{
|
{
|
||||||
@ -1067,6 +1064,26 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FunctionTypePointer StructType::constructorType() const
|
||||||
|
{
|
||||||
|
TypePointers paramTypes;
|
||||||
|
strings paramNames;
|
||||||
|
for (auto const& member: getMembers())
|
||||||
|
{
|
||||||
|
if (!member.type->canLiveOutsideStorage())
|
||||||
|
continue;
|
||||||
|
paramNames.push_back(member.name);
|
||||||
|
paramTypes.push_back(copyForLocationIfReference(DataLocation::Memory, member.type));
|
||||||
|
}
|
||||||
|
return make_shared<FunctionType>(
|
||||||
|
paramTypes,
|
||||||
|
TypePointers{copyForLocation(DataLocation::Memory, false)},
|
||||||
|
paramNames,
|
||||||
|
strings(),
|
||||||
|
FunctionType::Location::Internal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
pair<u256, unsigned> const& StructType::getStorageOffsetsOfMember(string const& _name) const
|
||||||
{
|
{
|
||||||
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
auto const* offsets = getMembers().getMemberStorageOffset(_name);
|
||||||
@ -1695,6 +1712,3 @@ string MagicType::toString(bool) const
|
|||||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
|
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Unknown kind of magic."));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
4
Types.h
4
Types.h
@ -557,6 +557,10 @@ public:
|
|||||||
|
|
||||||
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
TypePointer copyForLocation(DataLocation _location, bool _isPointer) const override;
|
||||||
|
|
||||||
|
/// @returns a function that peforms the type conversion between a list of struct members
|
||||||
|
/// and a memory struct of this type.
|
||||||
|
FunctionTypePointer constructorType() const;
|
||||||
|
|
||||||
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
std::pair<u256, unsigned> const& getStorageOffsetsOfMember(std::string const& _name) const;
|
||||||
u256 memoryOffsetOfMember(std::string const& _name) const;
|
u256 memoryOffsetOfMember(std::string const& _name) const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user