|
@ -76,6 +76,15 @@ void ContractDefinition::checkTypeRequirements() |
|
|
|
|
|
|
|
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) |
|
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) |
|
|
function->checkTypeRequirements(); |
|
|
function->checkTypeRequirements(); |
|
|
|
|
|
// check for duplicate declaration
|
|
|
|
|
|
set<string> functions; |
|
|
|
|
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) |
|
|
|
|
|
{ |
|
|
|
|
|
string signature = function->getCanonicalSignature(); |
|
|
|
|
|
if (functions.count(signature)) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Duplicate functions are not allowed.")); |
|
|
|
|
|
functions.insert(signature); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables) |
|
|
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables) |
|
|
variable->checkTypeRequirements(); |
|
|
variable->checkTypeRequirements(); |
|
@ -129,6 +138,7 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
|
|
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
|
|
// into the types
|
|
|
// into the types
|
|
|
map<string, FunctionDefinition const*> functions; |
|
|
map<string, FunctionDefinition const*> functions; |
|
|
|
|
|
set<string> functionNames; |
|
|
map<string, ModifierDefinition const*> modifiers; |
|
|
map<string, ModifierDefinition const*> modifiers; |
|
|
|
|
|
|
|
|
// We search from derived to base, so the stored item causes the error.
|
|
|
// We search from derived to base, so the stored item causes the error.
|
|
@ -141,7 +151,8 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
string const& name = function->getName(); |
|
|
string const& name = function->getName(); |
|
|
if (modifiers.count(name)) |
|
|
if (modifiers.count(name)) |
|
|
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); |
|
|
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); |
|
|
FunctionDefinition const*& override = functions[name]; |
|
|
FunctionDefinition const*& override = functions[function->getCanonicalSignature()]; |
|
|
|
|
|
functionNames.insert(name); |
|
|
if (!override) |
|
|
if (!override) |
|
|
override = function.get(); |
|
|
override = function.get(); |
|
|
else if (override->getVisibility() != function->getVisibility() || |
|
|
else if (override->getVisibility() != function->getVisibility() || |
|
@ -152,13 +163,13 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers()) |
|
|
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers()) |
|
|
{ |
|
|
{ |
|
|
string const& name = modifier->getName(); |
|
|
string const& name = modifier->getName(); |
|
|
if (functions.count(name)) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function.")); |
|
|
|
|
|
ModifierDefinition const*& override = modifiers[name]; |
|
|
ModifierDefinition const*& override = modifiers[name]; |
|
|
if (!override) |
|
|
if (!override) |
|
|
override = modifier.get(); |
|
|
override = modifier.get(); |
|
|
else if (ModifierType(*override) != ModifierType(*modifier)) |
|
|
else if (ModifierType(*override) != ModifierType(*modifier)) |
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); |
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); |
|
|
|
|
|
if (functionNames.count(name)) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -185,16 +196,21 @@ vector<pair<FixedHash<4>, FunctionTypePointer>> const& ContractDefinition::getIn |
|
|
if (!m_interfaceFunctionList) |
|
|
if (!m_interfaceFunctionList) |
|
|
{ |
|
|
{ |
|
|
set<string> functionsSeen; |
|
|
set<string> functionsSeen; |
|
|
|
|
|
set<string> signaturesSeen; |
|
|
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); |
|
|
m_interfaceFunctionList.reset(new vector<pair<FixedHash<4>, FunctionTypePointer>>()); |
|
|
for (ContractDefinition const* contract: getLinearizedBaseContracts()) |
|
|
for (ContractDefinition const* contract: getLinearizedBaseContracts()) |
|
|
{ |
|
|
{ |
|
|
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) |
|
|
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) |
|
|
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && functionsSeen.count(f->getName()) == 0) |
|
|
{ |
|
|
|
|
|
string functionSignature = f->getCanonicalSignature(); |
|
|
|
|
|
if (f->isPublic() && !f->isConstructor() && !f->getName().empty() && signaturesSeen.count(functionSignature) == 0) |
|
|
{ |
|
|
{ |
|
|
functionsSeen.insert(f->getName()); |
|
|
functionsSeen.insert(f->getName()); |
|
|
FixedHash<4> hash(dev::sha3(f->getCanonicalSignature())); |
|
|
signaturesSeen.insert(functionSignature); |
|
|
|
|
|
FixedHash<4> hash(dev::sha3(functionSignature)); |
|
|
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false))); |
|
|
m_interfaceFunctionList->push_back(make_pair(hash, make_shared<FunctionType>(*f, false))); |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) |
|
|
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) |
|
|
if (v->isPublic() && functionsSeen.count(v->getName()) == 0) |
|
|
if (v->isPublic() && functionsSeen.count(v->getName()) == 0) |
|
@ -467,7 +483,43 @@ void Return::checkTypeRequirements() |
|
|
|
|
|
|
|
|
void VariableDeclarationStatement::checkTypeRequirements() |
|
|
void VariableDeclarationStatement::checkTypeRequirements() |
|
|
{ |
|
|
{ |
|
|
|
|
|
<<<<<<< HEAD |
|
|
m_variable->checkTypeRequirements(); |
|
|
m_variable->checkTypeRequirements(); |
|
|
|
|
|
======= |
|
|
|
|
|
// Variables can be declared without type (with "var"), in which case the first assignment
|
|
|
|
|
|
// sets the type.
|
|
|
|
|
|
// Note that assignments before the first declaration are legal because of the special scoping
|
|
|
|
|
|
// rules inherited from JavaScript.
|
|
|
|
|
|
if (m_variable->getValue()) |
|
|
|
|
|
{ |
|
|
|
|
|
if (m_variable->getType()) |
|
|
|
|
|
{ |
|
|
|
|
|
std::cout << "getType() ok" << std::endl; |
|
|
|
|
|
m_variable->getValue()->expectType(*m_variable->getType()); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// no type declared and no previous assignment, infer the type
|
|
|
|
|
|
std::cout << "here's where called...." << std::endl; |
|
|
|
|
|
Identifier* identifier = dynamic_cast<Identifier*>(m_variable->getValue().get()); |
|
|
|
|
|
if (identifier) |
|
|
|
|
|
identifier->checkTypeRequirementsFromVariableDeclaration(); |
|
|
|
|
|
else |
|
|
|
|
|
m_variable->getValue()->checkTypeRequirements(); |
|
|
|
|
|
TypePointer type = m_variable->getValue()->getType(); |
|
|
|
|
|
if (type->getCategory() == Type::Category::IntegerConstant) |
|
|
|
|
|
{ |
|
|
|
|
|
auto intType = dynamic_pointer_cast<IntegerConstantType const>(type)->getIntegerType(); |
|
|
|
|
|
if (!intType) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(m_variable->getValue()->createTypeError("Invalid integer constant " + type->toString())); |
|
|
|
|
|
type = intType; |
|
|
|
|
|
} |
|
|
|
|
|
else if (type->getCategory() == Type::Category::Void) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(m_variable->createTypeError("var cannot be void type")); |
|
|
|
|
|
m_variable->setType(type); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
>>>>>>> implement overload resolution |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void Assignment::checkTypeRequirements() |
|
|
void Assignment::checkTypeRequirements() |
|
@ -544,10 +596,16 @@ void BinaryOperation::checkTypeRequirements() |
|
|
|
|
|
|
|
|
void FunctionCall::checkTypeRequirements() |
|
|
void FunctionCall::checkTypeRequirements() |
|
|
{ |
|
|
{ |
|
|
m_expression->checkTypeRequirements(); |
|
|
// we need to check arguments' type first as their info will be used by m_express(Identifier).
|
|
|
for (ASTPointer<Expression> const& argument: m_arguments) |
|
|
for (ASTPointer<Expression> const& argument: m_arguments) |
|
|
argument->checkTypeRequirements(); |
|
|
argument->checkTypeRequirements(); |
|
|
|
|
|
|
|
|
|
|
|
auto identifier = dynamic_cast<Identifier*>(m_expression.get()); |
|
|
|
|
|
if (identifier) |
|
|
|
|
|
identifier->checkTypeRequirementsWithFunctionCall(*this); |
|
|
|
|
|
else |
|
|
|
|
|
m_expression->checkTypeRequirements(); |
|
|
|
|
|
|
|
|
Type const* expressionType = m_expression->getType().get(); |
|
|
Type const* expressionType = m_expression->getType().get(); |
|
|
if (isTypeConversion()) |
|
|
if (isTypeConversion()) |
|
|
{ |
|
|
{ |
|
@ -617,6 +675,19 @@ void FunctionCall::checkTypeRequirements() |
|
|
else |
|
|
else |
|
|
m_type = functionType->getReturnParameterTypes().front(); |
|
|
m_type = functionType->getReturnParameterTypes().front(); |
|
|
} |
|
|
} |
|
|
|
|
|
else if (OverloadedFunctionType const* overloadedTypes = dynamic_cast<OverloadedFunctionType const*>(expressionType)) |
|
|
|
|
|
{ |
|
|
|
|
|
// this only applies to "x(3)" where x is assigned by "var x = f;" where f is an overloaded functions.
|
|
|
|
|
|
overloadedTypes->m_identifier->overloadResolution(*this); |
|
|
|
|
|
FunctionType const* functionType = dynamic_cast<FunctionType const*>(overloadedTypes->m_identifier->getType().get()); |
|
|
|
|
|
|
|
|
|
|
|
// @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 |
|
|
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); |
|
|
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); |
|
|
} |
|
|
} |
|
@ -709,16 +780,92 @@ void IndexAccess::checkTypeRequirements() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall) |
|
|
|
|
|
{ |
|
|
|
|
|
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); |
|
|
|
|
|
|
|
|
|
|
|
if (!m_referencedDeclaration) |
|
|
|
|
|
overloadResolution(_functionCall); |
|
|
|
|
|
|
|
|
|
|
|
checkTypeRequirements(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Identifier::checkTypeRequirementsFromVariableDeclaration() |
|
|
|
|
|
{ |
|
|
|
|
|
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); |
|
|
|
|
|
|
|
|
|
|
|
if (!m_referencedDeclaration) |
|
|
|
|
|
m_type = make_shared<OverloadedFunctionType>(m_overloadedDeclarations, this); |
|
|
|
|
|
else |
|
|
|
|
|
checkTypeRequirements(); |
|
|
|
|
|
|
|
|
|
|
|
m_isLValue = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void Identifier::checkTypeRequirements() |
|
|
void Identifier::checkTypeRequirements() |
|
|
{ |
|
|
{ |
|
|
|
|
|
// var x = f; TODO!
|
|
|
solAssert(m_referencedDeclaration, "Identifier not resolved."); |
|
|
solAssert(m_referencedDeclaration, "Identifier not resolved."); |
|
|
|
|
|
|
|
|
m_isLValue = m_referencedDeclaration->isLValue(); |
|
|
m_isLValue = m_referencedDeclaration->isLValue(); |
|
|
|
|
|
if (m_isLValue) |
|
|
|
|
|
std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; |
|
|
|
|
|
else |
|
|
|
|
|
std::cout << "Identifier: " << string(getName()) << " -> true" << std::endl; |
|
|
m_type = m_referencedDeclaration->getType(m_currentContract); |
|
|
m_type = m_referencedDeclaration->getType(m_currentContract); |
|
|
if (!m_type) |
|
|
if (!m_type) |
|
|
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); |
|
|
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void Identifier::overloadResolution(FunctionCall const& _functionCall) |
|
|
|
|
|
{ |
|
|
|
|
|
solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); |
|
|
|
|
|
solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); |
|
|
|
|
|
|
|
|
|
|
|
bool resolved = false; |
|
|
|
|
|
|
|
|
|
|
|
std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments(); |
|
|
|
|
|
std::vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.getNames(); |
|
|
|
|
|
|
|
|
|
|
|
if (argumentNames.empty()) |
|
|
|
|
|
{ |
|
|
|
|
|
// positional arguments
|
|
|
|
|
|
std::vector<Declaration const*> possibles; |
|
|
|
|
|
for (Declaration const* declaration: m_overloadedDeclarations) |
|
|
|
|
|
{ |
|
|
|
|
|
TypePointer const& function = declaration->getType(); |
|
|
|
|
|
auto const& functionType = dynamic_cast<FunctionType const&>(*function); |
|
|
|
|
|
TypePointers const& parameterTypes = functionType.getParameterTypes(); |
|
|
|
|
|
|
|
|
|
|
|
if (functionType.takesArbitraryParameters() || |
|
|
|
|
|
(arguments.size() == parameterTypes.size() && |
|
|
|
|
|
std::equal(arguments.cbegin(), arguments.cend(), parameterTypes.cbegin(), |
|
|
|
|
|
[](ASTPointer<Expression const> const& argument, TypePointer const& parameterType) |
|
|
|
|
|
{ |
|
|
|
|
|
return argument->getType()->isImplicitlyConvertibleTo(*parameterType); |
|
|
|
|
|
}))) |
|
|
|
|
|
possibles.push_back(declaration); |
|
|
|
|
|
} |
|
|
|
|
|
std::cout << "possibles: " << possibles.size() << std::endl; |
|
|
|
|
|
if (possibles.empty()) |
|
|
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); |
|
|
|
|
|
else if (std::none_of(possibles.cbegin() + 1, possibles.cend(), |
|
|
|
|
|
[&possibles](Declaration const* declaration) |
|
|
|
|
|
{ |
|
|
|
|
|
return declaration->getScope() == possibles.front()->getScope(); |
|
|
|
|
|
})) |
|
|
|
|
|
setReferencedDeclaration(*possibles.front()); |
|
|
|
|
|
else |
|
|
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); |
|
|
|
|
|
} |
|
|
|
|
|
else |
|
|
|
|
|
{ |
|
|
|
|
|
// named arguments
|
|
|
|
|
|
// TODO: don't support right now
|
|
|
|
|
|
// BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet."));
|
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void ElementaryTypeNameExpression::checkTypeRequirements() |
|
|
void ElementaryTypeNameExpression::checkTypeRequirements() |
|
|
{ |
|
|
{ |
|
|
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); |
|
|
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); |
|
|