mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
d4206b7cd0
quick fix on christian's rational change so that ubuntu will stop yelling be more specific with rational declaration for Windows sake rational in namespace correction for windows
1575 lines
53 KiB
C++
1575 lines
53 KiB
C++
/*
|
|
This file is part of cpp-ethereum.
|
|
|
|
cpp-ethereum is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
cpp-ethereum is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* @author Christian <c@ethdev.com>
|
|
* @date 2015
|
|
* Type analyzer and checker.
|
|
*/
|
|
|
|
#include <libsolidity/analysis/TypeChecker.h>
|
|
#include <memory>
|
|
#include <boost/range/adaptor/reversed.hpp>
|
|
#include <libsolidity/ast/AST.h>
|
|
#include <libevmasm/Assembly.h> // needed for inline assembly
|
|
#include <libsolidity/inlineasm/AsmCodeGen.h>
|
|
|
|
using namespace std;
|
|
using namespace dev;
|
|
using namespace dev::solidity;
|
|
|
|
|
|
bool TypeChecker::checkTypeRequirements(const ContractDefinition& _contract)
|
|
{
|
|
try
|
|
{
|
|
visit(_contract);
|
|
}
|
|
catch (FatalError const&)
|
|
{
|
|
// We got a fatal error which required to stop further type checking, but we can
|
|
// continue normally from here.
|
|
if (m_errors.empty())
|
|
throw; // Something is weird here, rather throw again.
|
|
}
|
|
return Error::containsOnlyWarnings(m_errors);
|
|
}
|
|
|
|
TypePointer const& TypeChecker::type(Expression const& _expression) const
|
|
{
|
|
solAssert(!!_expression.annotation().type, "Type requested but not present.");
|
|
return _expression.annotation().type;
|
|
}
|
|
|
|
TypePointer const& TypeChecker::type(VariableDeclaration const& _variable) const
|
|
{
|
|
solAssert(!!_variable.annotation().type, "Type requested but not present.");
|
|
return _variable.annotation().type;
|
|
}
|
|
|
|
bool TypeChecker::visit(ContractDefinition const& _contract)
|
|
{
|
|
m_scope = &_contract;
|
|
|
|
// We force our own visiting order here.
|
|
//@TODO structs will be visited again below, but it is probably fine.
|
|
ASTNode::listAccept(_contract.definedStructs(), *this);
|
|
ASTNode::listAccept(_contract.baseContracts(), *this);
|
|
|
|
checkContractDuplicateFunctions(_contract);
|
|
checkContractIllegalOverrides(_contract);
|
|
checkContractAbstractFunctions(_contract);
|
|
checkContractAbstractConstructors(_contract);
|
|
|
|
FunctionDefinition const* function = _contract.constructor();
|
|
if (function && !function->returnParameters().empty())
|
|
typeError(function->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
|
|
|
|
FunctionDefinition const* fallbackFunction = nullptr;
|
|
for (FunctionDefinition const* function: _contract.definedFunctions())
|
|
{
|
|
if (function->name().empty())
|
|
{
|
|
if (fallbackFunction)
|
|
{
|
|
auto err = make_shared<Error>(Error::Type::DeclarationError);
|
|
*err << errinfo_comment("Only one fallback function is allowed.");
|
|
m_errors.push_back(err);
|
|
}
|
|
else
|
|
{
|
|
fallbackFunction = function;
|
|
if (!fallbackFunction->parameters().empty())
|
|
typeError(fallbackFunction->parameterList().location(), "Fallback function cannot take parameters.");
|
|
}
|
|
}
|
|
if (!function->isImplemented())
|
|
_contract.annotation().isFullyImplemented = false;
|
|
}
|
|
|
|
ASTNode::listAccept(_contract.subNodes(), *this);
|
|
|
|
checkContractExternalTypeClashes(_contract);
|
|
// check for hash collisions in function signatures
|
|
set<FixedHash<4>> hashes;
|
|
for (auto const& it: _contract.interfaceFunctionList())
|
|
{
|
|
FixedHash<4> const& hash = it.first;
|
|
if (hashes.count(hash))
|
|
typeError(
|
|
_contract.location(),
|
|
string("Function signature hash collision for ") + it.second->externalSignature()
|
|
);
|
|
hashes.insert(hash);
|
|
}
|
|
|
|
if (_contract.isLibrary())
|
|
checkLibraryRequirements(_contract);
|
|
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
|
|
{
|
|
/// 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 (FunctionDefinition const* function: _contract.definedFunctions())
|
|
functions[function->name()].push_back(function);
|
|
|
|
// Constructor
|
|
if (functions[_contract.name()].size() > 1)
|
|
{
|
|
SecondarySourceLocation ssl;
|
|
auto it = ++functions[_contract.name()].begin();
|
|
for (; it != functions[_contract.name()].end(); ++it)
|
|
ssl.append("Another declaration is here:", (*it)->location());
|
|
|
|
auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
|
|
*err <<
|
|
errinfo_sourceLocation(functions[_contract.name()].front()->location()) <<
|
|
errinfo_comment("More than one constructor defined.") <<
|
|
errinfo_secondarySourceLocation(ssl);
|
|
m_errors.push_back(err);
|
|
}
|
|
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])))
|
|
{
|
|
auto err = make_shared<Error>(Error(Error::Type::DeclarationError));
|
|
*err <<
|
|
errinfo_sourceLocation(overloads[j]->location()) <<
|
|
errinfo_comment("Function with same name and arguments defined twice.") <<
|
|
errinfo_secondarySourceLocation(SecondarySourceLocation().append(
|
|
"Other declaration is here:", overloads[i]->location()));
|
|
m_errors.push_back(err);
|
|
}
|
|
}
|
|
}
|
|
|
|
void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract)
|
|
{
|
|
// Mapping from name to function definition (exactly one per argument type equality class) and
|
|
// flag to indicate whether it is fully implemented.
|
|
using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
|
|
map<string, vector<FunTypeAndFlag>> functions;
|
|
|
|
// Search from base to derived
|
|
for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
|
|
for (FunctionDefinition const* function: contract->definedFunctions())
|
|
{
|
|
auto& overloads = functions[function->name()];
|
|
FunctionTypePointer funType = make_shared<FunctionType>(*function);
|
|
auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
|
|
{
|
|
return funType->hasEqualArgumentTypes(*_funAndFlag.first);
|
|
});
|
|
if (it == overloads.end())
|
|
overloads.push_back(make_pair(funType, function->isImplemented()));
|
|
else if (it->second)
|
|
{
|
|
if (!function->isImplemented())
|
|
typeError(function->location(), "Redeclaring an already implemented function as abstract");
|
|
}
|
|
else if (function->isImplemented())
|
|
it->second = true;
|
|
}
|
|
|
|
// Set to not fully implemented if at least one flag is false.
|
|
for (auto const& it: functions)
|
|
for (auto const& funAndFlag: it.second)
|
|
if (!funAndFlag.second)
|
|
{
|
|
_contract.annotation().isFullyImplemented = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _contract)
|
|
{
|
|
set<ContractDefinition const*> argumentsNeeded;
|
|
// check that we get arguments for all base constructors that need it.
|
|
// If not mark the contract as abstract (not fully implemented)
|
|
|
|
vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
|
for (ContractDefinition const* contract: bases)
|
|
if (FunctionDefinition const* constructor = contract->constructor())
|
|
if (contract != &_contract && !constructor->parameters().empty())
|
|
argumentsNeeded.insert(contract);
|
|
|
|
for (ContractDefinition const* contract: bases)
|
|
{
|
|
if (FunctionDefinition const* constructor = contract->constructor())
|
|
for (auto const& modifier: constructor->modifiers())
|
|
{
|
|
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
|
&dereference(*modifier->name())
|
|
);
|
|
if (baseContract)
|
|
argumentsNeeded.erase(baseContract);
|
|
}
|
|
|
|
|
|
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
|
{
|
|
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
|
|
solAssert(baseContract, "");
|
|
if (!base->arguments().empty())
|
|
argumentsNeeded.erase(baseContract);
|
|
}
|
|
}
|
|
if (!argumentsNeeded.empty())
|
|
_contract.annotation().isFullyImplemented = false;
|
|
}
|
|
|
|
void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
|
|
{
|
|
// TODO unify this at a later point. for this we need to put the constness and the access specifier
|
|
// into the types
|
|
map<string, vector<FunctionDefinition const*>> functions;
|
|
map<string, ModifierDefinition const*> modifiers;
|
|
|
|
// We search from derived to base, so the stored item causes the error.
|
|
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
|
{
|
|
for (FunctionDefinition const* function: contract->definedFunctions())
|
|
{
|
|
if (function->isConstructor())
|
|
continue; // constructors can neither be overridden nor override anything
|
|
string const& name = function->name();
|
|
if (modifiers.count(name))
|
|
typeError(modifiers[name]->location(), "Override changes function to modifier.");
|
|
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->visibility() != function->visibility() ||
|
|
overriding->isDeclaredConst() != function->isDeclaredConst() ||
|
|
overridingType != functionType
|
|
)
|
|
typeError(overriding->location(), "Override changes extended function signature.");
|
|
}
|
|
functions[name].push_back(function);
|
|
}
|
|
for (ModifierDefinition const* modifier: contract->functionModifiers())
|
|
{
|
|
string const& name = modifier->name();
|
|
ModifierDefinition const*& override = modifiers[name];
|
|
if (!override)
|
|
override = modifier;
|
|
else if (ModifierType(*override) != ModifierType(*modifier))
|
|
typeError(override->location(), "Override changes modifier signature.");
|
|
if (!functions[name].empty())
|
|
typeError(override->location(), "Override changes modifier to function.");
|
|
}
|
|
}
|
|
}
|
|
|
|
void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
|
|
{
|
|
map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
|
|
for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
|
|
{
|
|
for (FunctionDefinition const* f: contract->definedFunctions())
|
|
if (f->isPartOfExternalInterface())
|
|
{
|
|
auto functionType = make_shared<FunctionType>(*f);
|
|
// under non error circumstances this should be true
|
|
if (functionType->interfaceFunctionType())
|
|
externalDeclarations[functionType->externalSignature()].push_back(
|
|
make_pair(f, functionType)
|
|
);
|
|
}
|
|
for (VariableDeclaration const* v: contract->stateVariables())
|
|
if (v->isPartOfExternalInterface())
|
|
{
|
|
auto functionType = make_shared<FunctionType>(*v);
|
|
// under non error circumstances this should be true
|
|
if (functionType->interfaceFunctionType())
|
|
externalDeclarations[functionType->externalSignature()].push_back(
|
|
make_pair(v, 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))
|
|
typeError(
|
|
it.second[j].first->location(),
|
|
"Function overload clash during conversion to external types for arguments."
|
|
);
|
|
}
|
|
|
|
void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
|
|
{
|
|
solAssert(_contract.isLibrary(), "");
|
|
if (!_contract.baseContracts().empty())
|
|
typeError(_contract.location(), "Library is not allowed to inherit.");
|
|
|
|
for (auto const& var: _contract.stateVariables())
|
|
if (!var->isConstant())
|
|
typeError(var->location(), "Library cannot have non-constant state variables");
|
|
}
|
|
|
|
void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
|
|
{
|
|
auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name()));
|
|
solAssert(base, "Base contract not available.");
|
|
|
|
if (base->isLibrary())
|
|
typeError(_inheritance.location(), "Libraries cannot be inherited from.");
|
|
|
|
auto const& arguments = _inheritance.arguments();
|
|
TypePointers parameterTypes = ContractType(*base).constructorType()->parameterTypes();
|
|
if (!arguments.empty() && parameterTypes.size() != arguments.size())
|
|
{
|
|
typeError(
|
|
_inheritance.location(),
|
|
"Wrong argument count for constructor call: " +
|
|
toString(arguments.size()) +
|
|
" arguments given but expected " +
|
|
toString(parameterTypes.size()) +
|
|
"."
|
|
);
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < arguments.size(); ++i)
|
|
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
|
typeError(
|
|
arguments[i]->location(),
|
|
"Invalid type for argument in constructor call. "
|
|
"Invalid implicit conversion from " +
|
|
type(*arguments[i])->toString() +
|
|
" to " +
|
|
parameterTypes[i]->toString() +
|
|
" requested."
|
|
);
|
|
}
|
|
|
|
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
|
|
{
|
|
ContractDefinition const* library = dynamic_cast<ContractDefinition const*>(
|
|
_usingFor.libraryName().annotation().referencedDeclaration
|
|
);
|
|
if (!library || !library->isLibrary())
|
|
typeError(_usingFor.libraryName().location(), "Library name expected.");
|
|
}
|
|
|
|
bool TypeChecker::visit(StructDefinition const& _struct)
|
|
{
|
|
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
|
|
if (!type(*member)->canBeStored())
|
|
typeError(member->location(), "Type cannot be used in struct.");
|
|
|
|
// Check recursion, fatal error if detected.
|
|
using StructPointer = StructDefinition const*;
|
|
using StructPointersSet = set<StructPointer>;
|
|
function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
|
|
{
|
|
if (_parents.count(_struct))
|
|
fatalTypeError(_struct->location(), "Recursive struct definition.");
|
|
StructPointersSet parents = _parents;
|
|
parents.insert(_struct);
|
|
for (ASTPointer<VariableDeclaration> const& member: _struct->members())
|
|
if (type(*member)->category() == Type::Category::Struct)
|
|
{
|
|
auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName());
|
|
check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents);
|
|
}
|
|
};
|
|
check(&_struct, StructPointersSet{});
|
|
|
|
ASTNode::listAccept(_struct.members(), *this);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(FunctionDefinition const& _function)
|
|
{
|
|
bool isLibraryFunction = dynamic_cast<ContractDefinition const&>(*_function.scope()).isLibrary();
|
|
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
|
|
{
|
|
if (!type(*var)->canLiveOutsideStorage())
|
|
typeError(var->location(), "Type is required to live outside storage.");
|
|
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
|
|
fatalTypeError(var->location(), "Internal type is not allowed for public or external functions.");
|
|
}
|
|
for (ASTPointer<ModifierInvocation> const& modifier: _function.modifiers())
|
|
visitManually(
|
|
*modifier,
|
|
_function.isConstructor() ?
|
|
dynamic_cast<ContractDefinition const&>(*_function.scope()).annotation().linearizedBaseContracts :
|
|
vector<ContractDefinition const*>()
|
|
);
|
|
if (_function.isImplemented())
|
|
_function.body().accept(*this);
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|
{
|
|
// 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.
|
|
|
|
// type is filled either by ReferencesResolver directly from the type name or by
|
|
// TypeChecker at the VariableDeclarationStatement level.
|
|
TypePointer varType = _variable.annotation().type;
|
|
solAssert(!!varType, "Failed to infer variable type.");
|
|
if (_variable.isConstant())
|
|
{
|
|
if (!dynamic_cast<ContractDefinition const*>(_variable.scope()))
|
|
typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
|
|
if (!_variable.value())
|
|
typeError(_variable.location(), "Uninitialized \"constant\" variable.");
|
|
if (!varType->isValueType())
|
|
{
|
|
bool constImplemented = false;
|
|
if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get()))
|
|
constImplemented = arrayType->isByteArray();
|
|
if (!constImplemented)
|
|
typeError(
|
|
_variable.location(),
|
|
"Illegal use of \"constant\" specifier. \"constant\" "
|
|
"is not yet implemented for this type."
|
|
);
|
|
}
|
|
}
|
|
if (_variable.value())
|
|
expectType(*_variable.value(), *varType);
|
|
if (!_variable.isStateVariable())
|
|
{
|
|
if (varType->dataStoredIn(DataLocation::Memory) || varType->dataStoredIn(DataLocation::CallData))
|
|
if (!varType->canLiveOutsideStorage())
|
|
typeError(_variable.location(), "Type " + varType->toString() + " is only valid in storage.");
|
|
}
|
|
else if (
|
|
_variable.visibility() >= VariableDeclaration::Visibility::Public &&
|
|
!FunctionType(_variable).interfaceFunctionType()
|
|
)
|
|
typeError(_variable.location(), "Internal type is not allowed for public state variables.");
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::visitManually(
|
|
ModifierInvocation const& _modifier,
|
|
vector<ContractDefinition const*> const& _bases
|
|
)
|
|
{
|
|
std::vector<ASTPointer<Expression>> const& arguments = _modifier.arguments();
|
|
for (ASTPointer<Expression> const& argument: arguments)
|
|
argument->accept(*this);
|
|
_modifier.name()->accept(*this);
|
|
|
|
auto const* declaration = &dereference(*_modifier.name());
|
|
vector<ASTPointer<VariableDeclaration>> emptyParameterList;
|
|
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
|
|
if (auto modifierDecl = dynamic_cast<ModifierDefinition const*>(declaration))
|
|
parameters = &modifierDecl->parameters();
|
|
else
|
|
// check parameters for Base constructors
|
|
for (ContractDefinition const* base: _bases)
|
|
if (declaration == base)
|
|
{
|
|
if (auto referencedConstructor = base->constructor())
|
|
parameters = &referencedConstructor->parameters();
|
|
else
|
|
parameters = &emptyParameterList;
|
|
break;
|
|
}
|
|
if (!parameters)
|
|
{
|
|
typeError(_modifier.location(), "Referenced declaration is neither modifier nor base class.");
|
|
return;
|
|
}
|
|
if (parameters->size() != arguments.size())
|
|
{
|
|
typeError(
|
|
_modifier.location(),
|
|
"Wrong argument count for modifier invocation: " +
|
|
toString(arguments.size()) +
|
|
" arguments given but expected " +
|
|
toString(parameters->size()) +
|
|
"."
|
|
);
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < _modifier.arguments().size(); ++i)
|
|
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i])))
|
|
typeError(
|
|
arguments[i]->location(),
|
|
"Invalid type for argument in modifier invocation. "
|
|
"Invalid implicit conversion from " +
|
|
type(*arguments[i])->toString() +
|
|
" to " +
|
|
type(*(*parameters)[i])->toString() +
|
|
" requested."
|
|
);
|
|
}
|
|
|
|
bool TypeChecker::visit(EventDefinition const& _eventDef)
|
|
{
|
|
unsigned numIndexed = 0;
|
|
for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters())
|
|
{
|
|
if (var->isIndexed())
|
|
numIndexed++;
|
|
if (_eventDef.isAnonymous() && numIndexed > 4)
|
|
typeError(_eventDef.location(), "More than 4 indexed arguments for anonymous event.");
|
|
else if (!_eventDef.isAnonymous() && numIndexed > 3)
|
|
typeError(_eventDef.location(), "More than 3 indexed arguments for event.");
|
|
if (!type(*var)->canLiveOutsideStorage())
|
|
typeError(var->location(), "Type is required to live outside storage.");
|
|
if (!type(*var)->interfaceType(false))
|
|
typeError(var->location(), "Internal type is not allowed as event parameter type.");
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
|
{
|
|
// Inline assembly does not have its own type-checking phase, so we just run the
|
|
// code-generator and see whether it produces any errors.
|
|
// External references have already been resolved in a prior stage and stored in the annotation.
|
|
assembly::CodeGenerator codeGen(_inlineAssembly.operations(), m_errors);
|
|
codeGen.typeCheck([&](assembly::Identifier const& _identifier, eth::Assembly& _assembly, assembly::CodeGenerator::IdentifierContext _context) {
|
|
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
|
|
if (ref == _inlineAssembly.annotation().externalReferences.end())
|
|
return false;
|
|
Declaration const* declaration = ref->second;
|
|
solAssert(!!declaration, "");
|
|
if (_context == assembly::CodeGenerator::IdentifierContext::RValue)
|
|
{
|
|
solAssert(!!declaration->type(), "Type of declaration required but not yet determined.");
|
|
unsigned pushes = 0;
|
|
if (dynamic_cast<FunctionDefinition const*>(declaration))
|
|
pushes = 1;
|
|
else if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
|
{
|
|
if (var->isConstant())
|
|
fatalTypeError(SourceLocation(), "Constant variables not yet implemented for inline assembly.");
|
|
if (var->isLocalVariable())
|
|
pushes = var->type()->sizeOnStack();
|
|
else if (var->type()->isValueType())
|
|
pushes = 1;
|
|
else
|
|
pushes = 2; // slot number, intra slot offset
|
|
}
|
|
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
|
{
|
|
if (!contract->isLibrary())
|
|
return false;
|
|
pushes = 1;
|
|
}
|
|
for (unsigned i = 0; i < pushes; ++i)
|
|
_assembly.append(u256(0)); // just to verify the stack height
|
|
}
|
|
else
|
|
{
|
|
// lvalue context
|
|
if (auto varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
|
{
|
|
if (!varDecl->isLocalVariable())
|
|
return false; // only local variables are inline-assemlby lvalues
|
|
for (unsigned i = 0; i < declaration->type()->sizeOnStack(); ++i)
|
|
_assembly.append(Instruction::POP); // remove value just to verify the stack height
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(IfStatement const& _ifStatement)
|
|
{
|
|
expectType(_ifStatement.condition(), BoolType());
|
|
_ifStatement.trueStatement().accept(*this);
|
|
if (_ifStatement.falseStatement())
|
|
_ifStatement.falseStatement()->accept(*this);
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(WhileStatement const& _whileStatement)
|
|
{
|
|
expectType(_whileStatement.condition(), BoolType());
|
|
_whileStatement.body().accept(*this);
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(ForStatement const& _forStatement)
|
|
{
|
|
if (_forStatement.initializationExpression())
|
|
_forStatement.initializationExpression()->accept(*this);
|
|
if (_forStatement.condition())
|
|
expectType(*_forStatement.condition(), BoolType());
|
|
if (_forStatement.loopExpression())
|
|
_forStatement.loopExpression()->accept(*this);
|
|
_forStatement.body().accept(*this);
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::endVisit(Return const& _return)
|
|
{
|
|
if (!_return.expression())
|
|
return;
|
|
ParameterList const* params = _return.annotation().functionReturnParameters;
|
|
if (!params)
|
|
{
|
|
typeError(_return.location(), "Return arguments not allowed.");
|
|
return;
|
|
}
|
|
TypePointers returnTypes;
|
|
for (auto const& var: params->parameters())
|
|
returnTypes.push_back(type(*var));
|
|
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_return.expression()).get()))
|
|
{
|
|
if (tupleType->components().size() != params->parameters().size())
|
|
typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
|
|
else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes)))
|
|
typeError(
|
|
_return.expression()->location(),
|
|
"Return argument type " +
|
|
type(*_return.expression())->toString() +
|
|
" is not implicitly convertible to expected type " +
|
|
TupleType(returnTypes).toString(false) +
|
|
"."
|
|
);
|
|
}
|
|
else if (params->parameters().size() != 1)
|
|
typeError(_return.location(), "Different number of arguments in return statement than in returns declaration.");
|
|
else
|
|
{
|
|
TypePointer const& expected = type(*params->parameters().front());
|
|
if (!type(*_return.expression())->isImplicitlyConvertibleTo(*expected))
|
|
typeError(
|
|
_return.expression()->location(),
|
|
"Return argument type " +
|
|
type(*_return.expression())->toString() +
|
|
" is not implicitly convertible to expected type (type of first return variable) " +
|
|
expected->toString() +
|
|
"."
|
|
);
|
|
}
|
|
}
|
|
|
|
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
|
|
{
|
|
if (!_statement.initialValue())
|
|
{
|
|
// No initial value is only permitted for single variables with specified type.
|
|
if (_statement.declarations().size() != 1 || !_statement.declarations().front())
|
|
fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
|
|
VariableDeclaration const& varDecl = *_statement.declarations().front();
|
|
if (!varDecl.annotation().type)
|
|
fatalTypeError(_statement.location(), "Assignment necessary for type detection.");
|
|
if (auto ref = dynamic_cast<ReferenceType const*>(type(varDecl).get()))
|
|
{
|
|
if (ref->dataStoredIn(DataLocation::Storage))
|
|
{
|
|
auto err = make_shared<Error>(Error::Type::Warning);
|
|
*err <<
|
|
errinfo_sourceLocation(varDecl.location()) <<
|
|
errinfo_comment("Uninitialized storage pointer. Did you mean '<type> memory " + varDecl.name() + "'?");
|
|
m_errors.push_back(err);
|
|
}
|
|
}
|
|
varDecl.accept(*this);
|
|
return false;
|
|
}
|
|
|
|
// Here we have an initial value and might have to derive some types before we can visit
|
|
// the variable declaration(s).
|
|
|
|
_statement.initialValue()->accept(*this);
|
|
TypePointers valueTypes;
|
|
if (auto tupleType = dynamic_cast<TupleType const*>(type(*_statement.initialValue()).get()))
|
|
valueTypes = tupleType->components();
|
|
else
|
|
valueTypes = TypePointers{type(*_statement.initialValue())};
|
|
|
|
// Determine which component is assigned to which variable.
|
|
// If numbers do not match, fill up if variables begin or end empty (not both).
|
|
vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
|
|
assignments.resize(valueTypes.size(), nullptr);
|
|
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
|
|
if (variables.empty())
|
|
{
|
|
if (!valueTypes.empty())
|
|
fatalTypeError(
|
|
_statement.location(),
|
|
"Too many components (" +
|
|
toString(valueTypes.size()) +
|
|
") in value for variable assignment (0) needed"
|
|
);
|
|
}
|
|
else if (valueTypes.size() != variables.size() && !variables.front() && !variables.back())
|
|
fatalTypeError(
|
|
_statement.location(),
|
|
"Wildcard both at beginning and end of variable declaration list is only allowed "
|
|
"if the number of components is equal."
|
|
);
|
|
size_t minNumValues = variables.size();
|
|
if (!variables.empty() && (!variables.back() || !variables.front()))
|
|
--minNumValues;
|
|
if (valueTypes.size() < minNumValues)
|
|
fatalTypeError(
|
|
_statement.location(),
|
|
"Not enough components (" +
|
|
toString(valueTypes.size()) +
|
|
") in value to assign all variables (" +
|
|
toString(minNumValues) + ")."
|
|
);
|
|
if (valueTypes.size() > variables.size() && variables.front() && variables.back())
|
|
fatalTypeError(
|
|
_statement.location(),
|
|
"Too many components (" +
|
|
toString(valueTypes.size()) +
|
|
") in value for variable assignment (" +
|
|
toString(minNumValues) +
|
|
" needed)."
|
|
);
|
|
bool fillRight = !variables.empty() && (!variables.back() || variables.front());
|
|
for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
|
|
if (fillRight)
|
|
assignments[i] = variables[i].get();
|
|
else
|
|
assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
|
|
|
|
for (size_t i = 0; i < assignments.size(); ++i)
|
|
{
|
|
if (!assignments[i])
|
|
continue;
|
|
VariableDeclaration const& var = *assignments[i];
|
|
solAssert(!var.value(), "Value has to be tied to statement.");
|
|
TypePointer const& valueComponentType = valueTypes[i];
|
|
solAssert(!!valueComponentType, "");
|
|
if (!var.annotation().type)
|
|
{
|
|
// Infer type from value.
|
|
solAssert(!var.typeName(), "");
|
|
var.annotation().type = valueComponentType->mobileType();
|
|
if (!var.annotation().type)
|
|
{
|
|
if (valueComponentType->category() == Type::Category::RationalNumber)
|
|
fatalTypeError(
|
|
_statement.initialValue()->location(),
|
|
"Invalid rational " +
|
|
valueComponentType->toString() +
|
|
" (absolute value too large or divison by zero)."
|
|
);
|
|
else
|
|
solAssert(false, "");
|
|
}
|
|
var.accept(*this);
|
|
}
|
|
else
|
|
{
|
|
var.accept(*this);
|
|
if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type))
|
|
{
|
|
if (
|
|
valueComponentType->category() == Type::Category::RationalNumber &&
|
|
dynamic_cast<RationalNumberType const&>(*valueComponentType).isFractional() &&
|
|
valueComponentType->mobileType()
|
|
)
|
|
typeError(
|
|
_statement.location(),
|
|
"Type " +
|
|
valueComponentType->toString() +
|
|
" is not implicitly convertible to expected type " +
|
|
var.annotation().type->toString() +
|
|
". Try converting to type " +
|
|
valueComponentType->mobileType()->toString() +
|
|
" or use an explicit conversion."
|
|
);
|
|
else
|
|
typeError(
|
|
_statement.location(),
|
|
"Type " +
|
|
valueComponentType->toString() +
|
|
" is not implicitly convertible to expected type " +
|
|
var.annotation().type->toString() +
|
|
"."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::endVisit(ExpressionStatement const& _statement)
|
|
{
|
|
if (type(_statement.expression())->category() == Type::Category::RationalNumber)
|
|
if (!dynamic_cast<RationalNumberType const&>(*type(_statement.expression())).mobileType())
|
|
typeError(_statement.expression().location(), "Invalid rational number.");
|
|
}
|
|
|
|
bool TypeChecker::visit(Conditional const& _conditional)
|
|
{
|
|
expectType(_conditional.condition(), BoolType());
|
|
|
|
_conditional.trueExpression().accept(*this);
|
|
_conditional.falseExpression().accept(*this);
|
|
|
|
TypePointer trueType = type(_conditional.trueExpression())->mobileType();
|
|
TypePointer falseType = type(_conditional.falseExpression())->mobileType();
|
|
|
|
TypePointer commonType = Type::commonType(trueType, falseType);
|
|
if (!commonType)
|
|
{
|
|
typeError(
|
|
_conditional.location(),
|
|
"True expression's type " +
|
|
trueType->toString() +
|
|
" doesn't match false expression's type " +
|
|
falseType->toString() +
|
|
"."
|
|
);
|
|
// even we can't find a common type, we have to set a type here,
|
|
// otherwise the upper statement will not be able to check the type.
|
|
commonType = trueType;
|
|
}
|
|
|
|
_conditional.annotation().type = commonType;
|
|
|
|
if (_conditional.annotation().lValueRequested)
|
|
typeError(
|
|
_conditional.location(),
|
|
"Conditional expression as left value is not supported yet."
|
|
);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(Assignment const& _assignment)
|
|
{
|
|
requireLValue(_assignment.leftHandSide());
|
|
TypePointer t = type(_assignment.leftHandSide());
|
|
_assignment.annotation().type = t;
|
|
if (TupleType const* tupleType = dynamic_cast<TupleType const*>(t.get()))
|
|
{
|
|
// Sequenced assignments of tuples is not valid, make the result a "void" type.
|
|
_assignment.annotation().type = make_shared<TupleType>();
|
|
expectType(_assignment.rightHandSide(), *tupleType);
|
|
}
|
|
else if (t->category() == Type::Category::Mapping)
|
|
{
|
|
typeError(_assignment.location(), "Mappings cannot be assigned to.");
|
|
_assignment.rightHandSide().accept(*this);
|
|
}
|
|
else if (_assignment.assignmentOperator() == Token::Assign)
|
|
expectType(_assignment.rightHandSide(), *t);
|
|
else
|
|
{
|
|
// compound assignment
|
|
_assignment.rightHandSide().accept(*this);
|
|
TypePointer resultType = t->binaryOperatorResult(
|
|
Token::AssignmentToBinaryOp(_assignment.assignmentOperator()),
|
|
type(_assignment.rightHandSide())
|
|
);
|
|
if (!resultType || *resultType != *t)
|
|
typeError(
|
|
_assignment.location(),
|
|
"Operator " +
|
|
string(Token::toString(_assignment.assignmentOperator())) +
|
|
" not compatible with types " +
|
|
t->toString() +
|
|
" and " +
|
|
type(_assignment.rightHandSide())->toString()
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(TupleExpression const& _tuple)
|
|
{
|
|
vector<ASTPointer<Expression>> const& components = _tuple.components();
|
|
TypePointers types;
|
|
|
|
if (_tuple.annotation().lValueRequested)
|
|
{
|
|
if (_tuple.isInlineArray())
|
|
fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
|
|
for (auto const& component: components)
|
|
if (component)
|
|
{
|
|
requireLValue(*component);
|
|
types.push_back(type(*component));
|
|
}
|
|
else
|
|
types.push_back(TypePointer());
|
|
if (components.size() == 1)
|
|
_tuple.annotation().type = type(*components[0]);
|
|
else
|
|
_tuple.annotation().type = make_shared<TupleType>(types);
|
|
// If some of the components are not LValues, the error is reported above.
|
|
_tuple.annotation().isLValue = true;
|
|
}
|
|
else
|
|
{
|
|
TypePointer inlineArrayType;
|
|
for (size_t i = 0; i < components.size(); ++i)
|
|
{
|
|
// Outside of an lvalue-context, the only situation where a component can be empty is (x,).
|
|
if (!components[i] && !(i == 1 && components.size() == 2))
|
|
fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
|
|
else if (components[i])
|
|
{
|
|
components[i]->accept(*this);
|
|
types.push_back(type(*components[i]));
|
|
if (_tuple.isInlineArray())
|
|
solAssert(!!types[i], "Inline array cannot have empty components");
|
|
if (i == 0 && _tuple.isInlineArray())
|
|
inlineArrayType = types[i]->mobileType();
|
|
else if (_tuple.isInlineArray() && inlineArrayType)
|
|
inlineArrayType = Type::commonType(inlineArrayType, types[i]->mobileType());
|
|
}
|
|
else
|
|
types.push_back(TypePointer());
|
|
}
|
|
if (_tuple.isInlineArray())
|
|
{
|
|
if (!inlineArrayType)
|
|
fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
|
|
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
|
|
}
|
|
else
|
|
{
|
|
if (components.size() == 1)
|
|
_tuple.annotation().type = type(*components[0]);
|
|
else
|
|
{
|
|
if (components.size() == 2 && !components[1])
|
|
types.pop_back();
|
|
_tuple.annotation().type = make_shared<TupleType>(types);
|
|
}
|
|
}
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(UnaryOperation const& _operation)
|
|
{
|
|
// Inc, Dec, Add, Sub, Not, BitNot, Delete
|
|
Token::Value op = _operation.getOperator();
|
|
if (op == Token::Value::Inc || op == Token::Value::Dec || op == Token::Value::Delete)
|
|
requireLValue(_operation.subExpression());
|
|
else
|
|
_operation.subExpression().accept(*this);
|
|
TypePointer const& subExprType = type(_operation.subExpression());
|
|
TypePointer t = type(_operation.subExpression())->unaryOperatorResult(op);
|
|
if (!t)
|
|
{
|
|
typeError(
|
|
_operation.location(),
|
|
"Unary operator " +
|
|
string(Token::toString(op)) +
|
|
" cannot be applied to type " +
|
|
subExprType->toString()
|
|
);
|
|
t = subExprType;
|
|
}
|
|
_operation.annotation().type = t;
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::endVisit(BinaryOperation const& _operation)
|
|
{
|
|
TypePointer const& leftType = type(_operation.leftExpression());
|
|
TypePointer const& rightType = type(_operation.rightExpression());
|
|
TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
|
|
if (!commonType)
|
|
{
|
|
typeError(
|
|
_operation.location(),
|
|
"Operator " +
|
|
string(Token::toString(_operation.getOperator())) +
|
|
" not compatible with types " +
|
|
leftType->toString() +
|
|
" and " +
|
|
rightType->toString()
|
|
);
|
|
commonType = leftType;
|
|
}
|
|
_operation.annotation().commonType = commonType;
|
|
_operation.annotation().type =
|
|
Token::isCompareOp(_operation.getOperator()) ?
|
|
make_shared<BoolType>() :
|
|
commonType;
|
|
}
|
|
|
|
bool TypeChecker::visit(FunctionCall const& _functionCall)
|
|
{
|
|
bool isPositionalCall = _functionCall.names().empty();
|
|
vector<ASTPointer<Expression const>> arguments = _functionCall.arguments();
|
|
vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names();
|
|
|
|
// We need to check arguments' type first as they will be needed for overload resolution.
|
|
shared_ptr<TypePointers> argumentTypes;
|
|
if (isPositionalCall)
|
|
argumentTypes = make_shared<TypePointers>();
|
|
for (ASTPointer<Expression const> const& argument: arguments)
|
|
{
|
|
argument->accept(*this);
|
|
// only store them for positional calls
|
|
if (isPositionalCall)
|
|
argumentTypes->push_back(type(*argument));
|
|
}
|
|
if (isPositionalCall)
|
|
_functionCall.expression().annotation().argumentTypes = move(argumentTypes);
|
|
|
|
_functionCall.expression().accept(*this);
|
|
TypePointer expressionType = type(_functionCall.expression());
|
|
|
|
if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get()))
|
|
{
|
|
_functionCall.annotation().isStructConstructorCall = (typeType->actualType()->category() == Type::Category::Struct);
|
|
_functionCall.annotation().isTypeConversion = !_functionCall.annotation().isStructConstructorCall;
|
|
}
|
|
else
|
|
_functionCall.annotation().isStructConstructorCall = _functionCall.annotation().isTypeConversion = false;
|
|
|
|
if (_functionCall.annotation().isTypeConversion)
|
|
{
|
|
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
|
|
TypePointer resultType = t.actualType();
|
|
if (arguments.size() != 1)
|
|
typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion.");
|
|
else if (!isPositionalCall)
|
|
typeError(_functionCall.location(), "Type conversion cannot allow named arguments.");
|
|
else
|
|
{
|
|
TypePointer const& argType = type(*arguments.front());
|
|
if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get()))
|
|
// do not change the data location when converting
|
|
// (data location cannot yet be specified for type conversions)
|
|
resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType);
|
|
if (!argType->isExplicitlyConvertibleTo(*resultType))
|
|
typeError(_functionCall.location(), "Explicit type conversion not allowed.");
|
|
}
|
|
_functionCall.annotation().type = resultType;
|
|
|
|
return false;
|
|
}
|
|
|
|
// Actual function call or struct constructor call.
|
|
|
|
FunctionTypePointer functionType;
|
|
|
|
/// For error message: Struct members that were removed during conversion to memory.
|
|
set<string> membersRemovedForStructConstructor;
|
|
if (_functionCall.annotation().isStructConstructorCall)
|
|
{
|
|
TypeType const& t = dynamic_cast<TypeType const&>(*expressionType);
|
|
auto const& structType = dynamic_cast<StructType const&>(*t.actualType());
|
|
functionType = structType.constructorType();
|
|
membersRemovedForStructConstructor = structType.membersMissingInMemory();
|
|
}
|
|
else
|
|
functionType = dynamic_pointer_cast<FunctionType const>(expressionType);
|
|
|
|
if (!functionType)
|
|
{
|
|
typeError(_functionCall.location(), "Type is not callable");
|
|
_functionCall.annotation().type = make_shared<TupleType>();
|
|
return false;
|
|
}
|
|
else if (functionType->returnParameterTypes().size() == 1)
|
|
_functionCall.annotation().type = functionType->returnParameterTypes().front();
|
|
else
|
|
_functionCall.annotation().type = make_shared<TupleType>(functionType->returnParameterTypes());
|
|
|
|
TypePointers parameterTypes = functionType->parameterTypes();
|
|
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
|
|
{
|
|
string msg =
|
|
"Wrong argument count for function call: " +
|
|
toString(arguments.size()) +
|
|
" arguments given but expected " +
|
|
toString(parameterTypes.size()) +
|
|
".";
|
|
// Extend error message in case we try to construct a struct with mapping member.
|
|
if (_functionCall.annotation().isStructConstructorCall && !membersRemovedForStructConstructor.empty())
|
|
{
|
|
msg += " Members that have to be skipped in memory:";
|
|
for (auto const& member: membersRemovedForStructConstructor)
|
|
msg += " " + member;
|
|
}
|
|
typeError(_functionCall.location(), msg);
|
|
}
|
|
else if (isPositionalCall)
|
|
{
|
|
// call by positional arguments
|
|
for (size_t i = 0; i < arguments.size(); ++i)
|
|
{
|
|
auto const& argType = type(*arguments[i]);
|
|
if (functionType->takesArbitraryParameters())
|
|
{
|
|
if (auto t = dynamic_cast<RationalNumberType const*>(argType.get()))
|
|
if (!t->mobileType())
|
|
typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero).");
|
|
}
|
|
else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
|
|
typeError(
|
|
arguments[i]->location(),
|
|
"Invalid type for argument in function call. "
|
|
"Invalid implicit conversion from " +
|
|
type(*arguments[i])->toString() +
|
|
" to " +
|
|
parameterTypes[i]->toString() +
|
|
" requested."
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// call by named arguments
|
|
auto const& parameterNames = functionType->parameterNames();
|
|
if (functionType->takesArbitraryParameters())
|
|
typeError(
|
|
_functionCall.location(),
|
|
"Named arguments cannnot be used for functions that take arbitrary parameters."
|
|
);
|
|
else if (parameterNames.size() > argumentNames.size())
|
|
typeError(_functionCall.location(), "Some argument names are missing.");
|
|
else if (parameterNames.size() < argumentNames.size())
|
|
typeError(_functionCall.location(), "Too many arguments.");
|
|
else
|
|
{
|
|
// check duplicate names
|
|
bool duplication = false;
|
|
for (size_t i = 0; i < argumentNames.size(); i++)
|
|
for (size_t j = i + 1; j < argumentNames.size(); j++)
|
|
if (*argumentNames[i] == *argumentNames[j])
|
|
{
|
|
duplication = true;
|
|
typeError(arguments[i]->location(), "Duplicate named argument.");
|
|
}
|
|
|
|
// check actual types
|
|
if (!duplication)
|
|
for (size_t i = 0; i < argumentNames.size(); i++)
|
|
{
|
|
bool found = false;
|
|
for (size_t j = 0; j < parameterNames.size(); j++)
|
|
if (parameterNames[j] == *argumentNames[i])
|
|
{
|
|
found = true;
|
|
// check type convertible
|
|
if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[j]))
|
|
typeError(
|
|
arguments[i]->location(),
|
|
"Invalid type for argument in function call. "
|
|
"Invalid implicit conversion from " +
|
|
type(*arguments[i])->toString() +
|
|
" to " +
|
|
parameterTypes[i]->toString() +
|
|
" requested."
|
|
);
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
typeError(
|
|
_functionCall.location(),
|
|
"Named argument does not match function declaration."
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::endVisit(NewExpression const& _newExpression)
|
|
{
|
|
TypePointer type = _newExpression.typeName().annotation().type;
|
|
solAssert(!!type, "Type name not resolved.");
|
|
|
|
if (auto contractName = dynamic_cast<UserDefinedTypeName const*>(&_newExpression.typeName()))
|
|
{
|
|
auto contract = dynamic_cast<ContractDefinition const*>(&dereference(*contractName));
|
|
|
|
if (!contract)
|
|
fatalTypeError(_newExpression.location(), "Identifier is not a contract.");
|
|
if (!contract->annotation().isFullyImplemented)
|
|
typeError(_newExpression.location(), "Trying to create an instance of an abstract contract.");
|
|
|
|
solAssert(!!m_scope, "");
|
|
m_scope->annotation().contractDependencies.insert(contract);
|
|
solAssert(
|
|
!contract->annotation().linearizedBaseContracts.empty(),
|
|
"Linearized base contracts not yet available."
|
|
);
|
|
if (contractDependenciesAreCyclic(*m_scope))
|
|
typeError(
|
|
_newExpression.location(),
|
|
"Circular reference for contract creation (cannot create instance of derived or same contract)."
|
|
);
|
|
|
|
auto contractType = make_shared<ContractType>(*contract);
|
|
TypePointers parameterTypes = contractType->constructorType()->parameterTypes();
|
|
_newExpression.annotation().type = make_shared<FunctionType>(
|
|
parameterTypes,
|
|
TypePointers{contractType},
|
|
strings(),
|
|
strings(),
|
|
FunctionType::Location::Creation
|
|
);
|
|
}
|
|
else if (type->category() == Type::Category::Array)
|
|
{
|
|
if (!type->canLiveOutsideStorage())
|
|
fatalTypeError(
|
|
_newExpression.typeName().location(),
|
|
"Type cannot live outside storage."
|
|
);
|
|
if (!type->isDynamicallySized())
|
|
typeError(
|
|
_newExpression.typeName().location(),
|
|
"Length has to be placed in parentheses after the array type for new expression."
|
|
);
|
|
type = ReferenceType::copyForLocationIfReference(DataLocation::Memory, type);
|
|
_newExpression.annotation().type = make_shared<FunctionType>(
|
|
TypePointers{make_shared<IntegerType>(256)},
|
|
TypePointers{type},
|
|
strings(),
|
|
strings(),
|
|
FunctionType::Location::ObjectCreation
|
|
);
|
|
}
|
|
else
|
|
fatalTypeError(_newExpression.location(), "Contract or array type expected.");
|
|
}
|
|
|
|
bool TypeChecker::visit(MemberAccess const& _memberAccess)
|
|
{
|
|
_memberAccess.expression().accept(*this);
|
|
TypePointer exprType = type(_memberAccess.expression());
|
|
ASTString const& memberName = _memberAccess.memberName();
|
|
|
|
// Retrieve the types of the arguments if this is used to call a function.
|
|
auto const& argumentTypes = _memberAccess.annotation().argumentTypes;
|
|
MemberList::MemberMap possibleMembers = exprType->members(m_scope).membersByName(memberName);
|
|
if (possibleMembers.size() > 1 && argumentTypes)
|
|
{
|
|
// do overload resolution
|
|
for (auto it = possibleMembers.begin(); it != possibleMembers.end();)
|
|
if (
|
|
it->type->category() == Type::Category::Function &&
|
|
!dynamic_cast<FunctionType const&>(*it->type).canTakeArguments(*argumentTypes, exprType)
|
|
)
|
|
it = possibleMembers.erase(it);
|
|
else
|
|
++it;
|
|
}
|
|
if (possibleMembers.size() == 0)
|
|
{
|
|
auto storageType = ReferenceType::copyForLocationIfReference(
|
|
DataLocation::Storage,
|
|
exprType
|
|
);
|
|
if (!storageType->members(m_scope).membersByName(memberName).empty())
|
|
fatalTypeError(
|
|
_memberAccess.location(),
|
|
"Member \"" + memberName + "\" is not available in " +
|
|
exprType->toString() +
|
|
" outside of storage."
|
|
);
|
|
fatalTypeError(
|
|
_memberAccess.location(),
|
|
"Member \"" + memberName + "\" not found or not visible "
|
|
"after argument-dependent lookup in " + exprType->toString()
|
|
);
|
|
}
|
|
else if (possibleMembers.size() > 1)
|
|
fatalTypeError(
|
|
_memberAccess.location(),
|
|
"Member \"" + memberName + "\" not unique "
|
|
"after argument-dependent lookup in " + exprType->toString()
|
|
);
|
|
|
|
auto& annotation = _memberAccess.annotation();
|
|
annotation.referencedDeclaration = possibleMembers.front().declaration;
|
|
annotation.type = possibleMembers.front().type;
|
|
|
|
if (auto funType = dynamic_cast<FunctionType const*>(annotation.type.get()))
|
|
if (funType->bound() && !exprType->isImplicitlyConvertibleTo(*funType->selfType()))
|
|
typeError(
|
|
_memberAccess.location(),
|
|
"Function \"" + memberName + "\" cannot be called on an object of type " +
|
|
exprType->toString() + " (expected " + funType->selfType()->toString() + ")"
|
|
);
|
|
|
|
if (exprType->category() == Type::Category::Struct)
|
|
annotation.isLValue = true;
|
|
else if (exprType->category() == Type::Category::Array)
|
|
{
|
|
auto const& arrayType(dynamic_cast<ArrayType const&>(*exprType));
|
|
annotation.isLValue = (
|
|
memberName == "length" &&
|
|
arrayType.location() == DataLocation::Storage &&
|
|
arrayType.isDynamicallySized()
|
|
);
|
|
}
|
|
else if (exprType->category() == Type::Category::FixedBytes)
|
|
annotation.isLValue = false;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(IndexAccess const& _access)
|
|
{
|
|
_access.baseExpression().accept(*this);
|
|
TypePointer baseType = type(_access.baseExpression());
|
|
TypePointer resultType;
|
|
bool isLValue = false;
|
|
Expression const* index = _access.indexExpression();
|
|
switch (baseType->category())
|
|
{
|
|
case Type::Category::Array:
|
|
{
|
|
ArrayType const& actualType = dynamic_cast<ArrayType const&>(*baseType);
|
|
if (!index)
|
|
typeError(_access.location(), "Index expression cannot be omitted.");
|
|
else if (actualType.isString())
|
|
{
|
|
typeError(_access.location(), "Index access for string is not possible.");
|
|
index->accept(*this);
|
|
}
|
|
else
|
|
{
|
|
expectType(*index, IntegerType(256));
|
|
if (auto numberType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
|
|
{
|
|
if (!numberType->isFractional()) // error is reported above
|
|
if (!actualType.isDynamicallySized() && actualType.length() <= numberType->literalValue(nullptr))
|
|
typeError(_access.location(), "Out of bounds array access.");
|
|
}
|
|
}
|
|
resultType = actualType.baseType();
|
|
isLValue = actualType.location() != DataLocation::CallData;
|
|
break;
|
|
}
|
|
case Type::Category::Mapping:
|
|
{
|
|
MappingType const& actualType = dynamic_cast<MappingType const&>(*baseType);
|
|
if (!index)
|
|
typeError(_access.location(), "Index expression cannot be omitted.");
|
|
else
|
|
expectType(*index, *actualType.keyType());
|
|
resultType = actualType.valueType();
|
|
isLValue = true;
|
|
break;
|
|
}
|
|
case Type::Category::TypeType:
|
|
{
|
|
TypeType const& typeType = dynamic_cast<TypeType const&>(*baseType);
|
|
if (!index)
|
|
resultType = make_shared<TypeType>(make_shared<ArrayType>(DataLocation::Memory, typeType.actualType()));
|
|
else
|
|
{
|
|
expectType(*index, IntegerType(256));
|
|
if (auto length = dynamic_cast<RationalNumberType const*>(type(*index).get()))
|
|
resultType = make_shared<TypeType>(make_shared<ArrayType>(
|
|
DataLocation::Memory,
|
|
typeType.actualType(),
|
|
length->literalValue(nullptr)
|
|
));
|
|
else
|
|
typeError(index->location(), "Integer constant expected.");
|
|
}
|
|
break;
|
|
}
|
|
case Type::Category::FixedBytes:
|
|
{
|
|
FixedBytesType const& bytesType = dynamic_cast<FixedBytesType const&>(*baseType);
|
|
if (!index)
|
|
typeError(_access.location(), "Index expression cannot be omitted.");
|
|
else
|
|
{
|
|
expectType(*index, IntegerType(256));
|
|
if (auto integerType = dynamic_cast<RationalNumberType const*>(type(*index).get()))
|
|
if (bytesType.numBytes() <= integerType->literalValue(nullptr))
|
|
typeError(_access.location(), "Out of bounds array access.");
|
|
}
|
|
resultType = make_shared<FixedBytesType>(1);
|
|
isLValue = false; // @todo this heavily depends on how it is embedded
|
|
break;
|
|
}
|
|
default:
|
|
fatalTypeError(
|
|
_access.baseExpression().location(),
|
|
"Indexed expression has to be a type, mapping or array (is " + baseType->toString() + ")"
|
|
);
|
|
}
|
|
_access.annotation().type = move(resultType);
|
|
_access.annotation().isLValue = isLValue;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool TypeChecker::visit(Identifier const& _identifier)
|
|
{
|
|
IdentifierAnnotation& annotation = _identifier.annotation();
|
|
if (!annotation.referencedDeclaration)
|
|
{
|
|
if (!annotation.argumentTypes)
|
|
fatalTypeError(_identifier.location(), "Unable to determine overloaded type.");
|
|
if (annotation.overloadedDeclarations.empty())
|
|
fatalTypeError(_identifier.location(), "No candidates for overload resolution found.");
|
|
else if (annotation.overloadedDeclarations.size() == 1)
|
|
annotation.referencedDeclaration = *annotation.overloadedDeclarations.begin();
|
|
else
|
|
{
|
|
vector<Declaration const*> candidates;
|
|
|
|
for (Declaration const* declaration: annotation.overloadedDeclarations)
|
|
{
|
|
TypePointer function = declaration->type();
|
|
solAssert(!!function, "Requested type not present.");
|
|
auto const* functionType = dynamic_cast<FunctionType const*>(function.get());
|
|
if (functionType && functionType->canTakeArguments(*annotation.argumentTypes))
|
|
candidates.push_back(declaration);
|
|
}
|
|
if (candidates.empty())
|
|
fatalTypeError(_identifier.location(), "No matching declaration found after argument-dependent lookup.");
|
|
else if (candidates.size() == 1)
|
|
annotation.referencedDeclaration = candidates.front();
|
|
else
|
|
fatalTypeError(_identifier.location(), "No unique declaration found after argument-dependent lookup.");
|
|
}
|
|
}
|
|
solAssert(
|
|
!!annotation.referencedDeclaration,
|
|
"Referenced declaration is null after overload resolution."
|
|
);
|
|
annotation.isLValue = annotation.referencedDeclaration->isLValue();
|
|
annotation.type = annotation.referencedDeclaration->type();
|
|
if (!annotation.type)
|
|
fatalTypeError(_identifier.location(), "Declaration referenced before type could be determined.");
|
|
return false;
|
|
}
|
|
|
|
void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
|
|
{
|
|
_expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeName()));
|
|
}
|
|
|
|
void TypeChecker::endVisit(Literal const& _literal)
|
|
{
|
|
_literal.annotation().type = Type::forLiteral(_literal);
|
|
if (!_literal.annotation().type)
|
|
fatalTypeError(_literal.location(), "Invalid literal value.");
|
|
}
|
|
|
|
bool TypeChecker::contractDependenciesAreCyclic(
|
|
ContractDefinition const& _contract,
|
|
std::set<ContractDefinition const*> const& _seenContracts
|
|
) const
|
|
{
|
|
// Naive depth-first search that remembers nodes already seen.
|
|
if (_seenContracts.count(&_contract))
|
|
return true;
|
|
set<ContractDefinition const*> seen(_seenContracts);
|
|
seen.insert(&_contract);
|
|
for (auto const* c: _contract.annotation().contractDependencies)
|
|
if (contractDependenciesAreCyclic(*c, seen))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
Declaration const& TypeChecker::dereference(Identifier const& _identifier) const
|
|
{
|
|
solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored.");
|
|
return *_identifier.annotation().referencedDeclaration;
|
|
}
|
|
|
|
Declaration const& TypeChecker::dereference(UserDefinedTypeName const& _typeName) const
|
|
{
|
|
solAssert(!!_typeName.annotation().referencedDeclaration, "Declaration not stored.");
|
|
return *_typeName.annotation().referencedDeclaration;
|
|
}
|
|
|
|
void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType)
|
|
{
|
|
_expression.accept(*this);
|
|
if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType))
|
|
{
|
|
if (
|
|
type(_expression)->category() == Type::Category::RationalNumber &&
|
|
dynamic_pointer_cast<RationalNumberType const>(type(_expression))->isFractional() &&
|
|
type(_expression)->mobileType()
|
|
)
|
|
typeError(
|
|
_expression.location(),
|
|
"Type " +
|
|
type(_expression)->toString() +
|
|
" is not implicitly convertible to expected type " +
|
|
_expectedType.toString() +
|
|
". Try converting to type " +
|
|
type(_expression)->mobileType()->toString() +
|
|
" or use an explicit conversion."
|
|
);
|
|
else
|
|
typeError(
|
|
_expression.location(),
|
|
"Type " +
|
|
type(_expression)->toString() +
|
|
" is not implicitly convertible to expected type " +
|
|
_expectedType.toString() +
|
|
"."
|
|
);
|
|
}
|
|
}
|
|
|
|
void TypeChecker::requireLValue(Expression const& _expression)
|
|
{
|
|
_expression.annotation().lValueRequested = true;
|
|
_expression.accept(*this);
|
|
if (!_expression.annotation().isLValue)
|
|
typeError(_expression.location(), "Expression has to be an lvalue.");
|
|
}
|
|
|
|
void TypeChecker::typeError(SourceLocation const& _location, string const& _description)
|
|
{
|
|
auto err = make_shared<Error>(Error::Type::TypeError);
|
|
*err <<
|
|
errinfo_sourceLocation(_location) <<
|
|
errinfo_comment(_description);
|
|
|
|
m_errors.push_back(err);
|
|
}
|
|
|
|
void TypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
|
|
{
|
|
typeError(_location, _description);
|
|
BOOST_THROW_EXCEPTION(FatalError());
|
|
}
|