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);
|
||||
|
||||
Type const* expressionType = m_expression->getType().get();
|
||||
TypePointer const& expressionType = m_expression->getType();
|
||||
FunctionTypePointer functionType;
|
||||
if (isTypeConversion())
|
||||
{
|
||||
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)
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Exactly one argument expected for explicit type conversion."));
|
||||
if (!isPositionalCall)
|
||||
@ -815,87 +814,106 @@ void FunctionCall::checkTypeRequirements(TypePointers const*)
|
||||
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType()))
|
||||
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed."));
|
||||
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
|
||||
// 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 structs
|
||||
if (functionType->getReturnParameterTypes().empty())
|
||||
m_type = make_shared<VoidType>();
|
||||
else
|
||||
m_type = functionType->getReturnParameterTypes().front();
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*expressionType);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*type.getActualType());
|
||||
functionType = structType.constructorType();
|
||||
}
|
||||
else
|
||||
functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
|
||||
|
||||
if (!functionType)
|
||||
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
|
||||
{
|
||||
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*)
|
||||
|
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<ASTString>> const& getNames() const { return m_names; }
|
||||
|
||||
/// Returns true if this is not an actual function call, but an explicit type conversion
|
||||
/// or constructor call.
|
||||
/// @returns true if this is not an actual function call, but an explicit type conversion.
|
||||
/// Returns false for struct constructor calls.
|
||||
bool isTypeConversion() const;
|
||||
/// @return true if this is a constructor call for a struct, i.e. StructName(...).
|
||||
bool isStructConstructorCall() const;
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_expression;
|
||||
|
@ -313,38 +313,66 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
using Location = FunctionType::Location;
|
||||
if (_functionCall.isTypeConversion())
|
||||
{
|
||||
//@todo struct construction
|
||||
solAssert(_functionCall.getArguments().size() == 1, "");
|
||||
solAssert(_functionCall.getNames().empty(), "");
|
||||
Expression const& firstArgument = *_functionCall.getArguments().front();
|
||||
firstArgument.accept(*this);
|
||||
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
|
||||
{
|
||||
FunctionType const& function = dynamic_cast<FunctionType const&>(*_functionCall.getExpression().getType());
|
||||
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, "");
|
||||
}
|
||||
|
||||
FunctionType const& function = *functionType;
|
||||
switch (function.getLocation())
|
||||
{
|
||||
case Location::Internal:
|
||||
|
30
Types.cpp
30
Types.cpp
@ -30,11 +30,8 @@
|
||||
#include <libsolidity/AST.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
void StorageOffsets::computeOffsets(TypePointers const& _types)
|
||||
{
|
||||
@ -1067,6 +1064,26 @@ TypePointer StructType::copyForLocation(DataLocation _location, bool _isPointer)
|
||||
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
|
||||
{
|
||||
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."));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
4
Types.h
4
Types.h
@ -557,6 +557,10 @@ public:
|
||||
|
||||
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;
|
||||
u256 memoryOffsetOfMember(std::string const& _name) const;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user