diff options
Diffstat (limited to 'libsolidity/analysis/TypeChecker.cpp')
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 1328 |
1 files changed, 1328 insertions, 0 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp new file mode 100644 index 00000000..6b12c57f --- /dev/null +++ b/libsolidity/analysis/TypeChecker.cpp @@ -0,0 +1,1328 @@ +/* + 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> + +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) +{ + // We force our own visiting order here. + 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(), "Non-empty \"returns\" directive for constructor."); + + FunctionDefinition const* fallbackFunction = nullptr; + for (ASTPointer<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.get(); + if (!fallbackFunction->parameters().empty()) + typeError(fallbackFunction->parameterList(), "Fallback function cannot take parameters."); + } + } + if (!function->isImplemented()) + _contract.annotation().isFullyImplemented = false; + } + + ASTNode::listAccept(_contract.stateVariables(), *this); + ASTNode::listAccept(_contract.events(), *this); + ASTNode::listAccept(_contract.functionModifiers(), *this); + ASTNode::listAccept(_contract.definedFunctions(), *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, + 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 (ASTPointer<FunctionDefinition> const& function: _contract.definedFunctions()) + functions[function->name()].push_back(function.get()); + + // 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 (ASTPointer<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, "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 (ASTPointer<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], "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, "Override changes extended function signature."); + } + functions[name].push_back(function.get()); + } + for (ASTPointer<ModifierDefinition> const& modifier: contract->functionModifiers()) + { + string const& name = modifier->name(); + ModifierDefinition const*& override = modifiers[name]; + if (!override) + override = modifier.get(); + else if (ModifierType(*override) != ModifierType(*modifier)) + typeError(*override, "Override changes modifier signature."); + if (!functions[name].empty()) + typeError(*override, "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 (ASTPointer<FunctionDefinition> const& f: contract->definedFunctions()) + if (f->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*f); + externalDeclarations[functionType->externalSignature()].push_back( + make_pair(f.get(), functionType) + ); + } + for (ASTPointer<VariableDeclaration> const& v: contract->stateVariables()) + if (v->isPartOfExternalInterface()) + { + auto functionType = make_shared<FunctionType>(*v); + externalDeclarations[functionType->externalSignature()].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)) + typeError( + *it.second[j].first, + "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, "Library is not allowed to inherit."); + + for (auto const& var: _contract.stateVariables()) + if (!var->isConstant()) + typeError(*var, "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, "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, + "Wrong argument count for constructor call: " + + toString(arguments.size()) + + " arguments given but expected " + + toString(parameterTypes.size()) + + "." + ); + + for (size_t i = 0; i < arguments.size(); ++i) + if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + typeError( + *arguments[i], + "Invalid type for argument in constructor call. " + "Invalid implicit conversion from " + + type(*arguments[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested." + ); +} + +bool TypeChecker::visit(StructDefinition const& _struct) +{ + for (ASTPointer<VariableDeclaration> const& member: _struct.members()) + if (!type(*member)->canBeStored()) + typeError(*member, "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, "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, "Type is required to live outside storage."); + if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) + fatalTypeError(*var, "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, "Illegal use of \"constant\" specifier."); + if (!_variable.value()) + typeError(_variable, "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, + "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, "Type " + varType->toString() + " is only valid in storage."); + } + else if ( + _variable.visibility() >= VariableDeclaration::Visibility::Public && + !FunctionType(_variable).interfaceFunctionType() + ) + typeError(_variable, "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, "Referenced declaration is neither modifier nor base class."); + if (parameters->size() != arguments.size()) + typeError( + _modifier, + "Wrong argument count for modifier invocation: " + + toString(arguments.size()) + + " arguments given but expected " + + toString(parameters->size()) + + "." + ); + for (size_t i = 0; i < _modifier.arguments().size(); ++i) + if (!type(*arguments[i])->isImplicitlyConvertibleTo(*type(*(*parameters)[i]))) + typeError( + *arguments[i], + "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, "More than 4 indexed arguments for anonymous event."); + else if (!_eventDef.isAnonymous() && numIndexed > 3) + typeError(_eventDef, "More than 3 indexed arguments for event."); + if (!type(*var)->canLiveOutsideStorage()) + typeError(*var, "Type is required to live outside storage."); + if (!type(*var)->interfaceType(false)) + typeError(*var, "Internal type is not allowed as event parameter type."); + } + 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, "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, "Different number of arguments in return statement than in returns declaration."); + else if (!tupleType->isImplicitlyConvertibleTo(TupleType(returnTypes))) + typeError( + *_return.expression(), + "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, "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(), + "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, "Assignment necessary for type detection."); + VariableDeclaration const& varDecl = *_statement.declarations().front(); + if (!varDecl.annotation().type) + fatalTypeError(_statement, "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, + "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, + "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, + "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, + "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(), ""); + if ( + valueComponentType->category() == Type::Category::IntegerConstant && + !dynamic_pointer_cast<IntegerConstantType const>(valueComponentType)->integerType() + ) + fatalTypeError(*_statement.initialValue(), "Invalid integer constant " + valueComponentType->toString() + "."); + var.annotation().type = valueComponentType->mobileType(); + var.accept(*this); + } + else + { + var.accept(*this); + if (!valueComponentType->isImplicitlyConvertibleTo(*var.annotation().type)) + typeError( + _statement, + "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::IntegerConstant) + if (!dynamic_pointer_cast<IntegerConstantType const>(type(_statement.expression()))->integerType()) + typeError(_statement.expression(), "Invalid integer constant."); +} + +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, "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, + "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) + { + for (auto const& component: components) + if (component) + { + requireLValue(*component); + types.push_back(type(*component)); + } + else + types.push_back(TypePointer()); + _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 + { + 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, "Tuple component cannot be empty."); + else if (components[i]) + { + components[i]->accept(*this); + types.push_back(type(*components[i])); + } + else + types.push_back(TypePointer()); + } + 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, + "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, + "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, "Exactly one argument expected for explicit type conversion."); + else if (!isPositionalCall) + typeError(_functionCall, "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, "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, "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 const& 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, 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<IntegerConstantType const*>(argType.get())) + if (!t->integerType()) + typeError(*arguments[i], "Integer constant too large."); + } + else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + typeError( + *arguments[i], + "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, + "Named arguments cannnot be used for functions that take arbitrary parameters." + ); + else if (parameterNames.size() > argumentNames.size()) + typeError(_functionCall, "Some argument names are missing."); + else if (parameterNames.size() < argumentNames.size()) + typeError(_functionCall, "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], "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], + "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, + "Named argument does not match function declaration." + ); + } + } + } + + return false; +} + +void TypeChecker::endVisit(NewExpression const& _newExpression) +{ + auto contract = dynamic_cast<ContractDefinition const*>(&dereference(_newExpression.contractName())); + + if (!contract) + fatalTypeError(_newExpression, "Identifier is not a contract."); + if (!contract->annotation().isFullyImplemented) + typeError(_newExpression, "Trying to create an instance of an abstract contract."); + + auto scopeContract = _newExpression.contractName().annotation().contractScope; + scopeContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." + ); + if (contractDependenciesAreCyclic(*scopeContract)) + typeError( + _newExpression, + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); + + auto contractType = make_shared<ContractType>(*contract); + TypePointers const& parameterTypes = contractType->constructorType()->parameterTypes(); + _newExpression.annotation().type = make_shared<FunctionType>( + parameterTypes, + TypePointers{contractType}, + strings(), + strings(), + FunctionType::Location::Creation + ); +} + +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().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) + ) + it = possibleMembers.erase(it); + else + ++it; + } + if (possibleMembers.size() == 0) + { + auto storageType = ReferenceType::copyForLocationIfReference( + DataLocation::Storage, + exprType + ); + if (!storageType->members().membersByName(memberName).empty()) + fatalTypeError( + _memberAccess, + "Member \"" + memberName + "\" is not available in " + + exprType->toString() + + " outside of storage." + ); + fatalTypeError( + _memberAccess, + "Member \"" + memberName + "\" not found or not visible " + "after argument-dependent lookup in " + exprType->toString() + ); + } + else if (possibleMembers.size() > 1) + fatalTypeError( + _memberAccess, + "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 (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() + ); + } + + 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, "Index expression cannot be omitted."); + else if (actualType.isString()) + { + typeError(_access, "Index access for string is not possible."); + index->accept(*this); + } + else + { + expectType(*index, IntegerType(256)); + if (auto integerType = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + if (!actualType.isDynamicallySized() && actualType.length() <= integerType->literalValue(nullptr)) + typeError(_access, "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, "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 + { + index->accept(*this); + if (auto length = dynamic_cast<IntegerConstantType const*>(type(*index).get())) + resultType = make_shared<TypeType>(make_shared<ArrayType>( + DataLocation::Memory, + typeType.actualType(), + length->literalValue(nullptr) + )); + else + typeError(*index, "Integer constant expected."); + } + break; + } + default: + fatalTypeError( + _access.baseExpression(), + "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, "Unable to determine overloaded type."); + if (annotation.overloadedDeclarations.empty()) + fatalTypeError(_identifier, "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(_identifier.annotation().contractScope); + 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, "No matching declaration found after argument-dependent lookup."); + else if (candidates.size() == 1) + annotation.referencedDeclaration = candidates.front(); + else + fatalTypeError(_identifier, "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(_identifier.annotation().contractScope); + if (!annotation.type) + fatalTypeError(_identifier, "Declaration referenced before type could be determined."); + return false; +} + +void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr) +{ + _expr.annotation().type = make_shared<TypeType>(Type::fromElementaryTypeName(_expr.typeToken())); +} + +void TypeChecker::endVisit(Literal const& _literal) +{ + _literal.annotation().type = Type::forLiteral(_literal); + if (!_literal.annotation().type) + fatalTypeError(_literal, "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) +{ + solAssert(!!_identifier.annotation().referencedDeclaration, "Declaration not stored."); + return *_identifier.annotation().referencedDeclaration; +} + +void TypeChecker::expectType(Expression const& _expression, Type const& _expectedType) +{ + _expression.accept(*this); + + if (!type(_expression)->isImplicitlyConvertibleTo(_expectedType)) + typeError( + _expression, + "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, "Expression has to be an lvalue."); +} + +void TypeChecker::typeError(ASTNode const& _node, string const& _description) +{ + auto err = make_shared<Error>(Error::Type::TypeError); + *err << + errinfo_sourceLocation(_node.location()) << + errinfo_comment(_description); + + m_errors.push_back(err); +} + +void TypeChecker::fatalTypeError(ASTNode const& _node, string const& _description) +{ + typeError(_node, _description); + BOOST_THROW_EXCEPTION(FatalError()); +} |