Struct constructors.

This commit is contained in:
chriseth 2015-06-30 21:08:34 +02:00
parent 6059d20750
commit e2d6e34f9c
5 changed files with 175 additions and 109 deletions

168
AST.cpp
View File

@ -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
View File

@ -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;

View File

@ -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:

View File

@ -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."));
}
}
}
}

View File

@ -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;