|
|
@ -52,6 +52,7 @@ void ContractDefinition::checkTypeRequirements() |
|
|
|
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts()) |
|
|
|
baseSpecifier->checkTypeRequirements(); |
|
|
|
|
|
|
|
checkDuplicateFunctions(); |
|
|
|
checkIllegalOverrides(); |
|
|
|
checkAbstractFunctions(); |
|
|
|
|
|
|
@ -82,20 +83,11 @@ void ContractDefinition::checkTypeRequirements() |
|
|
|
|
|
|
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) |
|
|
|
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_sourceLocation(function->getLocation()) |
|
|
|
<< errinfo_comment("Duplicate functions are not allowed.")); |
|
|
|
functions.insert(signature); |
|
|
|
} |
|
|
|
|
|
|
|
for (ASTPointer<VariableDeclaration> const& variable: m_stateVariables) |
|
|
|
variable->checkTypeRequirements(); |
|
|
|
|
|
|
|
checkExternalTypeClashes(); |
|
|
|
// check for hash collisions in function signatures
|
|
|
|
set<FixedHash<4>> hashes; |
|
|
|
for (auto const& it: getInterfaceFunctionList()) |
|
|
@ -140,6 +132,33 @@ FunctionDefinition const* ContractDefinition::getFallbackFunction() const |
|
|
|
return nullptr; |
|
|
|
} |
|
|
|
|
|
|
|
void ContractDefinition::checkDuplicateFunctions() const |
|
|
|
{ |
|
|
|
/// Checks that two functions with the same name defined in this contract have different
|
|
|
|
/// argument types and that there is at most one constructor.
|
|
|
|
map<string, vector<FunctionDefinition const*>> functions; |
|
|
|
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions()) |
|
|
|
functions[function->getName()].push_back(function.get()); |
|
|
|
if (functions[getName()].size() > 1) |
|
|
|
BOOST_THROW_EXCEPTION( |
|
|
|
DeclarationError() << |
|
|
|
errinfo_sourceLocation(getLocation()) << |
|
|
|
errinfo_comment("More than one constructor defined.") |
|
|
|
); |
|
|
|
for (auto const& it: functions) |
|
|
|
{ |
|
|
|
vector<FunctionDefinition const*> const& overloads = it.second; |
|
|
|
for (size_t i = 0; i < overloads.size(); ++i) |
|
|
|
for (size_t j = i + 1; j < overloads.size(); ++j) |
|
|
|
if (FunctionType(*overloads[i]).hasEqualArgumentTypes(FunctionType(*overloads[j]))) |
|
|
|
BOOST_THROW_EXCEPTION( |
|
|
|
DeclarationError() << |
|
|
|
errinfo_sourceLocation(overloads[j]->getLocation()) << |
|
|
|
errinfo_comment("Function with same name and arguments already defined.") |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void ContractDefinition::checkAbstractFunctions() |
|
|
|
{ |
|
|
|
map<string, bool> functions; |
|
|
@ -166,8 +185,7 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
|
{ |
|
|
|
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
|
|
|
// into the types
|
|
|
|
map<string, FunctionDefinition const*> functions; |
|
|
|
set<string> functionNames; |
|
|
|
map<string, vector<FunctionDefinition const*>> functions; |
|
|
|
map<string, ModifierDefinition const*> modifiers; |
|
|
|
|
|
|
|
// We search from derived to base, so the stored item causes the error.
|
|
|
@ -180,14 +198,21 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
|
string const& name = function->getName(); |
|
|
|
if (modifiers.count(name)) |
|
|
|
BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier.")); |
|
|
|
FunctionDefinition const*& override = functions[function->getCanonicalSignature()]; |
|
|
|
functionNames.insert(name); |
|
|
|
if (!override) |
|
|
|
override = function.get(); |
|
|
|
else if (override->getVisibility() != function->getVisibility() || |
|
|
|
override->isDeclaredConst() != function->isDeclaredConst() || |
|
|
|
FunctionType(*override) != FunctionType(*function)) |
|
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature.")); |
|
|
|
FunctionType functionType(*function); |
|
|
|
// function should not change the return type
|
|
|
|
for (FunctionDefinition const* overriding: functions[name]) |
|
|
|
{ |
|
|
|
FunctionType overridingType(*overriding); |
|
|
|
if (!overridingType.hasEqualArgumentTypes(functionType)) |
|
|
|
continue; |
|
|
|
if ( |
|
|
|
overriding->getVisibility() != function->getVisibility() || |
|
|
|
overriding->isDeclaredConst() != function->isDeclaredConst() || |
|
|
|
overridingType != functionType |
|
|
|
) |
|
|
|
BOOST_THROW_EXCEPTION(overriding->createTypeError("Override changes extended function signature.")); |
|
|
|
} |
|
|
|
functions[name].push_back(function.get()); |
|
|
|
} |
|
|
|
for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers()) |
|
|
|
{ |
|
|
@ -197,12 +222,43 @@ void ContractDefinition::checkIllegalOverrides() const |
|
|
|
override = modifier.get(); |
|
|
|
else if (ModifierType(*override) != ModifierType(*modifier)) |
|
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature.")); |
|
|
|
if (functionNames.count(name)) |
|
|
|
if (!functions[name].empty()) |
|
|
|
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier to function.")); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void ContractDefinition::checkExternalTypeClashes() const |
|
|
|
{ |
|
|
|
map<string, vector<pair<Declaration const*, shared_ptr<FunctionType>>>> externalDeclarations; |
|
|
|
for (ContractDefinition const* contract: getLinearizedBaseContracts()) |
|
|
|
{ |
|
|
|
for (ASTPointer<FunctionDefinition> const& f: contract->getDefinedFunctions()) |
|
|
|
if (f->isPartOfExternalInterface()) |
|
|
|
{ |
|
|
|
auto functionType = make_shared<FunctionType>(*f); |
|
|
|
externalDeclarations[functionType->externalSignature(f->getName())].push_back( |
|
|
|
make_pair(f.get(), functionType) |
|
|
|
); |
|
|
|
} |
|
|
|
for (ASTPointer<VariableDeclaration> const& v: contract->getStateVariables()) |
|
|
|
if (v->isPartOfExternalInterface()) |
|
|
|
{ |
|
|
|
auto functionType = make_shared<FunctionType>(*v); |
|
|
|
externalDeclarations[functionType->externalSignature(v->getName())].push_back( |
|
|
|
make_pair(v.get(), functionType) |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
for (auto const& it: externalDeclarations) |
|
|
|
for (size_t i = 0; i < it.second.size(); ++i) |
|
|
|
for (size_t j = i + 1; j < it.second.size(); ++j) |
|
|
|
if (!it.second[i].second->hasEqualArgumentTypes(*it.second[j].second)) |
|
|
|
BOOST_THROW_EXCEPTION(it.second[j].first->createTypeError( |
|
|
|
"Function overload clash during conversion to external types for arguments." |
|
|
|
)); |
|
|
|
} |
|
|
|
|
|
|
|
std::vector<ASTPointer<EventDefinition>> const& ContractDefinition::getInterfaceEvents() const |
|
|
|
{ |
|
|
|
if (!m_interfaceEvents) |
|
|
@ -291,11 +347,11 @@ TypePointer EnumValue::getType(ContractDefinition const*) const |
|
|
|
|
|
|
|
void InheritanceSpecifier::checkTypeRequirements() |
|
|
|
{ |
|
|
|
m_baseName->checkTypeRequirements(); |
|
|
|
m_baseName->checkTypeRequirements(nullptr); |
|
|
|
for (ASTPointer<Expression> const& argument: m_arguments) |
|
|
|
argument->checkTypeRequirements(); |
|
|
|
argument->checkTypeRequirements(nullptr); |
|
|
|
|
|
|
|
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(m_baseName->getReferencedDeclaration()); |
|
|
|
ContractDefinition const* base = dynamic_cast<ContractDefinition const*>(&m_baseName->getReferencedDeclaration()); |
|
|
|
solAssert(base, "Base contract not available."); |
|
|
|
TypePointers parameterTypes = ContractType(*base).getConstructorType()->getParameterTypes(); |
|
|
|
if (parameterTypes.size() != m_arguments.size()) |
|
|
@ -409,11 +465,7 @@ void VariableDeclaration::checkTypeRequirements() |
|
|
|
else |
|
|
|
{ |
|
|
|
// no type declared and no previous assignment, infer the type
|
|
|
|
Identifier* identifier = dynamic_cast<Identifier*>(m_value.get()); |
|
|
|
if (identifier) |
|
|
|
identifier->checkTypeRequirementsFromVariableDeclaration(); |
|
|
|
else |
|
|
|
m_value->checkTypeRequirements(); |
|
|
|
m_value->checkTypeRequirements(nullptr); |
|
|
|
|
|
|
|
TypePointer type = m_value->getType(); |
|
|
|
if (type->getCategory() == Type::Category::IntegerConstant) |
|
|
@ -452,11 +504,15 @@ void ModifierDefinition::checkTypeRequirements() |
|
|
|
|
|
|
|
void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpecifier>> const& _bases) |
|
|
|
{ |
|
|
|
m_modifierName->checkTypeRequirements(); |
|
|
|
TypePointers argumentTypes; |
|
|
|
for (ASTPointer<Expression> const& argument: m_arguments) |
|
|
|
argument->checkTypeRequirements(); |
|
|
|
{ |
|
|
|
argument->checkTypeRequirements(nullptr); |
|
|
|
argumentTypes.push_back(argument->getType()); |
|
|
|
} |
|
|
|
m_modifierName->checkTypeRequirements(&argumentTypes); |
|
|
|
|
|
|
|
auto declaration = m_modifierName->getReferencedDeclaration(); |
|
|
|
auto const* declaration = &m_modifierName->getReferencedDeclaration(); |
|
|
|
vector<ASTPointer<VariableDeclaration>> emptyParameterList; |
|
|
|
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr; |
|
|
|
if (auto modifier = dynamic_cast<ModifierDefinition const*>(declaration)) |
|
|
@ -464,7 +520,7 @@ void ModifierInvocation::checkTypeRequirements(vector<ASTPointer<InheritanceSpec |
|
|
|
else |
|
|
|
// check parameters for Base constructors
|
|
|
|
for (auto const& base: _bases) |
|
|
|
if (declaration == base->getName()->getReferencedDeclaration()) |
|
|
|
if (declaration == &base->getName()->getReferencedDeclaration()) |
|
|
|
{ |
|
|
|
if (auto referencedConstructor = dynamic_cast<ContractDefinition const&>(*declaration).getConstructor()) |
|
|
|
parameters = &referencedConstructor->getParameters(); |
|
|
@ -547,9 +603,9 @@ void VariableDeclarationStatement::checkTypeRequirements() |
|
|
|
m_variable->checkTypeRequirements(); |
|
|
|
} |
|
|
|
|
|
|
|
void Assignment::checkTypeRequirements() |
|
|
|
void Assignment::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_leftHandSide->checkTypeRequirements(); |
|
|
|
m_leftHandSide->checkTypeRequirements(nullptr); |
|
|
|
m_leftHandSide->requireLValue(); |
|
|
|
if (m_leftHandSide->getType()->getCategory() == Type::Category::Mapping) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Mappings cannot be assigned to.")); |
|
|
@ -559,7 +615,7 @@ void Assignment::checkTypeRequirements() |
|
|
|
else |
|
|
|
{ |
|
|
|
// compound assignment
|
|
|
|
m_rightHandSide->checkTypeRequirements(); |
|
|
|
m_rightHandSide->checkTypeRequirements(nullptr); |
|
|
|
TypePointer resultType = m_type->binaryOperatorResult(Token::AssignmentToBinaryOp(m_assigmentOperator), |
|
|
|
m_rightHandSide->getType()); |
|
|
|
if (!resultType || *resultType != *m_type) |
|
|
@ -572,7 +628,7 @@ void Assignment::checkTypeRequirements() |
|
|
|
|
|
|
|
void ExpressionStatement::checkTypeRequirements() |
|
|
|
{ |
|
|
|
m_expression->checkTypeRequirements(); |
|
|
|
m_expression->checkTypeRequirements(nullptr); |
|
|
|
if (m_expression->getType()->getCategory() == Type::Category::IntegerConstant) |
|
|
|
if (!dynamic_pointer_cast<IntegerConstantType const>(m_expression->getType())->getIntegerType()) |
|
|
|
BOOST_THROW_EXCEPTION(m_expression->createTypeError("Invalid integer constant.")); |
|
|
@ -580,7 +636,7 @@ void ExpressionStatement::checkTypeRequirements() |
|
|
|
|
|
|
|
void Expression::expectType(Type const& _expectedType) |
|
|
|
{ |
|
|
|
checkTypeRequirements(); |
|
|
|
checkTypeRequirements(nullptr); |
|
|
|
Type const& type = *getType(); |
|
|
|
if (!type.isImplicitlyConvertibleTo(_expectedType)) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Type " + type.toString() + |
|
|
@ -595,10 +651,10 @@ void Expression::requireLValue() |
|
|
|
m_lvalueRequested = true; |
|
|
|
} |
|
|
|
|
|
|
|
void UnaryOperation::checkTypeRequirements() |
|
|
|
void UnaryOperation::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
// Inc, Dec, Add, Sub, Not, BitNot, Delete
|
|
|
|
m_subExpression->checkTypeRequirements(); |
|
|
|
m_subExpression->checkTypeRequirements(nullptr); |
|
|
|
if (m_operator == Token::Value::Inc || m_operator == Token::Value::Dec || m_operator == Token::Value::Delete) |
|
|
|
m_subExpression->requireLValue(); |
|
|
|
m_type = m_subExpression->getType()->unaryOperatorResult(m_operator); |
|
|
@ -606,10 +662,10 @@ void UnaryOperation::checkTypeRequirements() |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Unary operator not compatible with type.")); |
|
|
|
} |
|
|
|
|
|
|
|
void BinaryOperation::checkTypeRequirements() |
|
|
|
void BinaryOperation::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_left->checkTypeRequirements(); |
|
|
|
m_right->checkTypeRequirements(); |
|
|
|
m_left->checkTypeRequirements(nullptr); |
|
|
|
m_right->checkTypeRequirements(nullptr); |
|
|
|
m_commonType = m_left->getType()->binaryOperatorResult(m_operator, m_right->getType()); |
|
|
|
if (!m_commonType) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Operator " + string(Token::toString(m_operator)) + |
|
|
@ -619,17 +675,22 @@ void BinaryOperation::checkTypeRequirements() |
|
|
|
m_type = Token::isCompareOp(m_operator) ? make_shared<BoolType>() : m_commonType; |
|
|
|
} |
|
|
|
|
|
|
|
void FunctionCall::checkTypeRequirements() |
|
|
|
void FunctionCall::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
// we need to check arguments' type first as their info will be used by m_express(Identifier).
|
|
|
|
bool isPositionalCall = m_names.empty(); |
|
|
|
|
|
|
|
// we need to check arguments' type first as they will be forwarded to
|
|
|
|
// m_expression->checkTypeRequirements
|
|
|
|
TypePointers argumentTypes; |
|
|
|
for (ASTPointer<Expression> const& argument: m_arguments) |
|
|
|
argument->checkTypeRequirements(); |
|
|
|
{ |
|
|
|
argument->checkTypeRequirements(nullptr); |
|
|
|
// only store them for positional calls
|
|
|
|
if (isPositionalCall) |
|
|
|
argumentTypes.push_back(argument->getType()); |
|
|
|
} |
|
|
|
|
|
|
|
auto identifier = dynamic_cast<Identifier*>(m_expression.get()); |
|
|
|
if (identifier) |
|
|
|
identifier->checkTypeRequirementsWithFunctionCall(*this); |
|
|
|
else |
|
|
|
m_expression->checkTypeRequirements(); |
|
|
|
m_expression->checkTypeRequirements(isPositionalCall ? &argumentTypes : nullptr); |
|
|
|
|
|
|
|
Type const* expressionType = m_expression->getType().get(); |
|
|
|
if (isTypeConversion()) |
|
|
@ -639,7 +700,7 @@ void FunctionCall::checkTypeRequirements() |
|
|
|
// number of non-mapping members
|
|
|
|
if (m_arguments.size() != 1) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("More than one argument for explicit type conversion.")); |
|
|
|
if (!m_names.empty()) |
|
|
|
if (!isPositionalCall) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Type conversion cannot allow named arguments.")); |
|
|
|
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type.getActualType())) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Explicit type conversion not allowed.")); |
|
|
@ -654,8 +715,9 @@ void FunctionCall::checkTypeRequirements() |
|
|
|
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != m_arguments.size()) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for function call.")); |
|
|
|
|
|
|
|
if (m_names.empty()) |
|
|
|
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])) |
|
|
@ -663,6 +725,7 @@ void FunctionCall::checkTypeRequirements() |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// call by named arguments
|
|
|
|
if (functionType->takesArbitraryParameters()) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Named arguments cannnot be used for functions " |
|
|
|
"that take arbitrary parameters.")); |
|
|
@ -700,27 +763,6 @@ void FunctionCall::checkTypeRequirements() |
|
|
|
else |
|
|
|
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.
|
|
|
|
auto identifier = dynamic_cast<Identifier*>(m_expression.get()); |
|
|
|
solAssert(identifier, "only applies to 'var x = f;'"); |
|
|
|
|
|
|
|
Declaration const* function = overloadedTypes->getIdentifier()->overloadResolution(*this); |
|
|
|
if (!function) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve declarations")); |
|
|
|
|
|
|
|
identifier->setReferencedDeclaration(*function); |
|
|
|
identifier->checkTypeRequirements(); |
|
|
|
|
|
|
|
TypePointer type = identifier->getType(); |
|
|
|
FunctionType const* functionType = dynamic_cast<FunctionType const*>(type.get()); |
|
|
|
|
|
|
|
if (functionType->getReturnParameterTypes().empty()) |
|
|
|
m_type = make_shared<VoidType>(); |
|
|
|
else |
|
|
|
m_type = functionType->getReturnParameterTypes().front(); |
|
|
|
} |
|
|
|
else |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Type is not callable.")); |
|
|
|
} |
|
|
@ -730,10 +772,10 @@ bool FunctionCall::isTypeConversion() const |
|
|
|
return m_expression->getType()->getCategory() == Type::Category::TypeType; |
|
|
|
} |
|
|
|
|
|
|
|
void NewExpression::checkTypeRequirements() |
|
|
|
void NewExpression::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_contractName->checkTypeRequirements(); |
|
|
|
m_contract = dynamic_cast<ContractDefinition const*>(m_contractName->getReferencedDeclaration()); |
|
|
|
m_contractName->checkTypeRequirements(nullptr); |
|
|
|
m_contract = dynamic_cast<ContractDefinition const*>(&m_contractName->getReferencedDeclaration()); |
|
|
|
if (!m_contract) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Identifier is not a contract.")); |
|
|
|
if (!m_contract->isFullyImplemented()) |
|
|
@ -744,15 +786,37 @@ void NewExpression::checkTypeRequirements() |
|
|
|
FunctionType::Location::Creation); |
|
|
|
} |
|
|
|
|
|
|
|
void MemberAccess::checkTypeRequirements() |
|
|
|
void MemberAccess::checkTypeRequirements(TypePointers const* _argumentTypes) |
|
|
|
{ |
|
|
|
m_expression->checkTypeRequirements(); |
|
|
|
m_expression->checkTypeRequirements(nullptr); |
|
|
|
Type const& type = *m_expression->getType(); |
|
|
|
m_type = type.getMemberType(*m_memberName); |
|
|
|
if (!m_type) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " |
|
|
|
"visible in " + type.toString())); |
|
|
|
// This should probably move somewhere else.
|
|
|
|
|
|
|
|
MemberList::MemberMap possibleMembers = type.getMembers().membersByName(*m_memberName); |
|
|
|
if (possibleMembers.size() > 1 && _argumentTypes) |
|
|
|
{ |
|
|
|
// do override resolution
|
|
|
|
for (auto it = possibleMembers.begin(); it != possibleMembers.end();) |
|
|
|
if ( |
|
|
|
it->type->getCategory() == Type::Category::Function && |
|
|
|
!dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*_argumentTypes) |
|
|
|
) |
|
|
|
it = possibleMembers.erase(it); |
|
|
|
else |
|
|
|
++it; |
|
|
|
} |
|
|
|
if (possibleMembers.size() == 0) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError( |
|
|
|
"Member \"" + *m_memberName + "\" not found or not visible " |
|
|
|
"after argument-dependent lookup in " + type.toString() |
|
|
|
)); |
|
|
|
else if (possibleMembers.size() > 1) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError( |
|
|
|
"Member \"" + *m_memberName + "\" not unique " |
|
|
|
"after argument-dependent lookup in " + type.toString() |
|
|
|
)); |
|
|
|
|
|
|
|
m_referencedDeclaration = possibleMembers.front().declaration; |
|
|
|
m_type = possibleMembers.front().type; |
|
|
|
if (type.getCategory() == Type::Category::Struct) |
|
|
|
m_isLValue = true; |
|
|
|
else if (type.getCategory() == Type::Category::Array) |
|
|
@ -765,9 +829,9 @@ void MemberAccess::checkTypeRequirements() |
|
|
|
m_isLValue = false; |
|
|
|
} |
|
|
|
|
|
|
|
void IndexAccess::checkTypeRequirements() |
|
|
|
void IndexAccess::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_base->checkTypeRequirements(); |
|
|
|
m_base->checkTypeRequirements(nullptr); |
|
|
|
switch (m_base->getType()->getCategory()) |
|
|
|
{ |
|
|
|
case Type::Category::Array: |
|
|
@ -800,7 +864,7 @@ void IndexAccess::checkTypeRequirements() |
|
|
|
m_type = make_shared<TypeType>(make_shared<ArrayType>(ArrayType::Location::Memory, type.getActualType())); |
|
|
|
else |
|
|
|
{ |
|
|
|
m_index->checkTypeRequirements(); |
|
|
|
m_index->checkTypeRequirements(nullptr); |
|
|
|
auto length = dynamic_cast<IntegerConstantType const*>(m_index->getType().get()); |
|
|
|
if (!length) |
|
|
|
BOOST_THROW_EXCEPTION(m_index->createTypeError("Integer constant expected.")); |
|
|
@ -815,89 +879,57 @@ void IndexAccess::checkTypeRequirements() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void Identifier::checkTypeRequirementsWithFunctionCall(FunctionCall const& _functionCall) |
|
|
|
{ |
|
|
|
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); |
|
|
|
|
|
|
|
if (!m_referencedDeclaration) |
|
|
|
setReferencedDeclaration(*overloadResolution(_functionCall)); |
|
|
|
|
|
|
|
checkTypeRequirements(); |
|
|
|
} |
|
|
|
|
|
|
|
void Identifier::checkTypeRequirementsFromVariableDeclaration() |
|
|
|
void Identifier::checkTypeRequirements(TypePointers const* _argumentTypes) |
|
|
|
{ |
|
|
|
solAssert(m_referencedDeclaration || !m_overloadedDeclarations.empty(), "Identifier not resolved."); |
|
|
|
|
|
|
|
if (!m_referencedDeclaration) |
|
|
|
m_type = make_shared<OverloadedFunctionType>(this); |
|
|
|
else |
|
|
|
checkTypeRequirements(); |
|
|
|
|
|
|
|
m_isLValue = true; |
|
|
|
} |
|
|
|
|
|
|
|
void Identifier::checkTypeRequirements() |
|
|
|
{ |
|
|
|
solAssert(m_referencedDeclaration, "Identifier not resolved."); |
|
|
|
|
|
|
|
{ |
|
|
|
if (!_argumentTypes) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Unable to determine overloaded type.")); |
|
|
|
overloadResolution(*_argumentTypes); |
|
|
|
} |
|
|
|
solAssert(!!m_referencedDeclaration, "Referenced declaration is null after overload resolution."); |
|
|
|
m_isLValue = m_referencedDeclaration->isLValue(); |
|
|
|
m_type = m_referencedDeclaration->getType(m_currentContract); |
|
|
|
if (!m_type) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined.")); |
|
|
|
} |
|
|
|
|
|
|
|
Declaration const* Identifier::overloadResolution(FunctionCall const& _functionCall) |
|
|
|
Declaration const& Identifier::getReferencedDeclaration() const |
|
|
|
{ |
|
|
|
solAssert(!!m_referencedDeclaration, "Identifier not resolved."); |
|
|
|
return *m_referencedDeclaration; |
|
|
|
} |
|
|
|
|
|
|
|
void Identifier::overloadResolution(TypePointers const& _argumentTypes) |
|
|
|
{ |
|
|
|
solAssert(m_overloadedDeclarations.size() > 1, "FunctionIdentifier not resolved."); |
|
|
|
solAssert(!m_referencedDeclaration, "Referenced declaration should be null before overload resolution."); |
|
|
|
solAssert(!m_overloadedDeclarations.empty(), "No candidates for overload resolution found."); |
|
|
|
|
|
|
|
std::vector<ASTPointer<Expression const>> arguments = _functionCall.getArguments(); |
|
|
|
std::vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.getNames(); |
|
|
|
std::vector<Declaration const*> possibles; |
|
|
|
if (m_overloadedDeclarations.size() == 1) |
|
|
|
m_referencedDeclaration = *m_overloadedDeclarations.begin(); |
|
|
|
|
|
|
|
if (argumentNames.empty()) |
|
|
|
for (Declaration const* declaration: m_overloadedDeclarations) |
|
|
|
{ |
|
|
|
// 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); |
|
|
|
} |
|
|
|
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(); |
|
|
|
})) |
|
|
|
return possibles.front(); |
|
|
|
else |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Can't resolve identifier")); |
|
|
|
TypePointer const& function = declaration->getType(); |
|
|
|
auto const* functionType = dynamic_cast<FunctionType const*>(function.get()); |
|
|
|
if (functionType && functionType->canTakeArguments(_argumentTypes)) |
|
|
|
possibles.push_back(declaration); |
|
|
|
} |
|
|
|
if (possibles.size() == 1) |
|
|
|
m_referencedDeclaration = possibles.front(); |
|
|
|
else if (possibles.empty()) |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("No matching declaration found after argument-dependent lookup.")); |
|
|
|
else |
|
|
|
// named arguments
|
|
|
|
// TODO: don't support right now
|
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("Named arguments with overloaded functions are not supported yet.")); |
|
|
|
return nullptr; |
|
|
|
BOOST_THROW_EXCEPTION(createTypeError("No unique declaration found after argument-dependent lookup.")); |
|
|
|
} |
|
|
|
|
|
|
|
void ElementaryTypeNameExpression::checkTypeRequirements() |
|
|
|
void ElementaryTypeNameExpression::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_type = make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); |
|
|
|
} |
|
|
|
|
|
|
|
void Literal::checkTypeRequirements() |
|
|
|
void Literal::checkTypeRequirements(TypePointers const*) |
|
|
|
{ |
|
|
|
m_type = Type::forLiteral(*this); |
|
|
|
if (!m_type) |
|
|
|