diff options
author | Kristofer Peterson <kris@tranception.com> | 2018-10-19 05:53:59 +0800 |
---|---|---|
committer | svenski123 <svenski123@users.noreply.github.com> | 2018-11-10 00:35:54 +0800 |
commit | f927da9182f6225d5ab354d4224b842262756a07 (patch) | |
tree | bc3e2c91ca64fd5fd29312e33e6b215876892dbf | |
parent | 9709dfe04633ddb5e7d9911cdef1f4de54e5592e (diff) | |
download | dexon-solidity-f927da9182f6225d5ab354d4224b842262756a07.tar.gz dexon-solidity-f927da9182f6225d5ab354d4224b842262756a07.tar.zst dexon-solidity-f927da9182f6225d5ab354d4224b842262756a07.zip |
Refactor of bool TypeChecker::visit(FunctionCall const& _functionCall).
Visit method now cleanly determines if node represents a function call,
struct construction or type conversion. Type checking, validation and
error message logic is moved to separate methods.
5 files changed, 532 insertions, 278 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 3774cf86..c5e6488b 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -582,7 +582,7 @@ TypePointers TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall c if (!actualType->fullEncodingType(false, _abiEncoderV2, false)) m_errorReporter.typeError( typeArgument->location(), - "Decoding type " + actualType->toString(false) + " not supported." + "Decoding type " + actualType->toString(false) + " not supported." ); components.push_back(actualType); } @@ -1681,352 +1681,570 @@ void TypeChecker::endVisit(BinaryOperation const& _operation) } } -bool TypeChecker::visit(FunctionCall const& _functionCall) +TypePointer TypeChecker::typeCheckTypeConversionAndRetrieveReturnType( + FunctionCall const& _functionCall +) { - bool isPositionalCall = _functionCall.names().empty(); - vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); - vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); - - bool isPure = true; - - // 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); - if (!argument->annotation().isPure) - isPure = false; - // only store them for positional calls - if (isPositionalCall) - argumentTypes->push_back(type(*argument)); - } - if (isPositionalCall) - _functionCall.expression().annotation().argumentTypes = move(argumentTypes); + solAssert(_functionCall.annotation().kind == FunctionCallKind::TypeConversion, ""); + TypePointer const& expressionType = type(_functionCall.expression()); - _functionCall.expression().accept(*this); - TypePointer expressionType = type(_functionCall.expression()); + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + bool const isPositionalCall = _functionCall.names().empty(); - if (auto const* typeType = dynamic_cast<TypeType const*>(expressionType.get())) - { - if (typeType->actualType()->category() == Type::Category::Struct) - _functionCall.annotation().kind = FunctionCallKind::StructConstructorCall; - else - _functionCall.annotation().kind = FunctionCallKind::TypeConversion; - - } + TypePointer resultType = dynamic_cast<TypeType const&>(*expressionType).actualType(); + if (arguments.size() != 1) + m_errorReporter.typeError( + _functionCall.location(), + "Exactly one argument expected for explicit type conversion." + ); + else if (!isPositionalCall) + m_errorReporter.typeError( + _functionCall.location(), + "Type conversion cannot allow named arguments." + ); else - _functionCall.annotation().kind = FunctionCallKind::FunctionCall; - solAssert(_functionCall.annotation().kind != FunctionCallKind::Unset, ""); - - if (_functionCall.annotation().kind == FunctionCallKind::TypeConversion) { - TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); - TypePointer resultType = t.actualType(); - if (arguments.size() != 1) - m_errorReporter.typeError(_functionCall.location(), "Exactly one argument expected for explicit type conversion."); - else if (!isPositionalCall) - m_errorReporter.typeError(_functionCall.location(), "Type conversion cannot allow named arguments."); - else + TypePointer const& argType = type(*arguments.front()); + // Resulting data location is memory unless we are converting from a reference + // type with a different data location. + // (data location cannot yet be specified for type conversions) + DataLocation dataLoc = DataLocation::Memory; + if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) + dataLoc = argRefType->location(); + if (auto type = dynamic_cast<ReferenceType const*>(resultType.get())) + resultType = type->copyForLocation(dataLoc, type->isPointer()); + if (argType->isExplicitlyConvertibleTo(*resultType)) { - TypePointer const& argType = type(*arguments.front()); - // Resulting data location is memory unless we are converting from a reference - // type with a different data location. - // (data location cannot yet be specified for type conversions) - DataLocation dataLoc = DataLocation::Memory; - if (auto argRefType = dynamic_cast<ReferenceType const*>(argType.get())) - dataLoc = argRefType->location(); - if (auto type = dynamic_cast<ReferenceType const*>(resultType.get())) - resultType = type->copyForLocation(dataLoc, type->isPointer()); - if (argType->isExplicitlyConvertibleTo(*resultType)) - { - if (auto argArrayType = dynamic_cast<ArrayType const*>(argType.get())) - { - auto resultArrayType = dynamic_cast<ArrayType const*>(resultType.get()); - solAssert(!!resultArrayType, ""); - solAssert( - argArrayType->location() != DataLocation::Storage || - ((resultArrayType->isPointer() || (argArrayType->isByteArray() && resultArrayType->isByteArray())) && - resultArrayType->location() == DataLocation::Storage), - "Invalid explicit conversion to storage type." - ); - } - } - else + if (auto argArrayType = dynamic_cast<ArrayType const*>(argType.get())) { - if (resultType->category() == Type::Category::Contract && argType->category() == Type::Category::Address) - { - solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), ""); - solAssert(dynamic_cast<AddressType const*>(argType.get())->stateMutability() < StateMutability::Payable, ""); - SecondarySourceLocation ssl; - if (auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get())) - if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration)) - ssl.append("Did you mean to declare this variable as \"address payable\"?", variableDeclaration->location()); - m_errorReporter.typeError( - _functionCall.location(), ssl, - "Explicit type conversion not allowed from non-payable \"address\" to \"" + - resultType->toString() + - "\", which has a payable fallback function." - ); - } - else - m_errorReporter.typeError( - _functionCall.location(), - "Explicit type conversion not allowed from \"" + - argType->toString() + - "\" to \"" + - resultType->toString() + - "\"." - ); + auto resultArrayType = dynamic_cast<ArrayType const*>(resultType.get()); + solAssert(!!resultArrayType, ""); + solAssert( + argArrayType->location() != DataLocation::Storage || + ( + ( + resultArrayType->isPointer() || + (argArrayType->isByteArray() && resultArrayType->isByteArray()) + ) && + resultArrayType->location() == DataLocation::Storage + ), + "Invalid explicit conversion to storage type." + ); } - if (resultType->category() == Type::Category::Address) + } + else + { + if ( + resultType->category() == Type::Category::Contract && + argType->category() == Type::Category::Address + ) { - bool payable = argType->isExplicitlyConvertibleTo(AddressType::addressPayable()); - resultType = make_shared<AddressType>(payable ? StateMutability::Payable : StateMutability::NonPayable); + solAssert(dynamic_cast<ContractType const*>(resultType.get())->isPayable(), ""); + solAssert( + dynamic_cast<AddressType const*>(argType.get())->stateMutability() < + StateMutability::Payable, + "" + ); + SecondarySourceLocation ssl; + if ( + auto const* identifier = dynamic_cast<Identifier const*>(arguments.front().get()) + ) + if ( + auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>( + identifier->annotation().referencedDeclaration + ) + ) + ssl.append( + "Did you mean to declare this variable as \"address payable\"?", + variableDeclaration->location() + ); + m_errorReporter.typeError( + _functionCall.location(), ssl, + "Explicit type conversion not allowed from non-payable \"address\" to \"" + + resultType->toString() + + "\", which has a payable fallback function." + ); } + else + m_errorReporter.typeError( + _functionCall.location(), + "Explicit type conversion not allowed from \"" + + argType->toString() + + "\" to \"" + + resultType->toString() + + "\"." + ); + } + if (resultType->category() == Type::Category::Address) + { + bool const payable = argType->isExplicitlyConvertibleTo(AddressType::addressPayable()); + resultType = make_shared<AddressType>( + payable ? StateMutability::Payable : StateMutability::NonPayable + ); } - _functionCall.annotation().type = resultType; - _functionCall.annotation().isPure = isPure; - - return false; } + return resultType; +} +void TypeChecker::typeCheckFunctionCall( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ // Actual function call or struct constructor call. - FunctionTypePointer functionType; + solAssert(!!_functionType, ""); + solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); - /// For error message: Struct members that were removed during conversion to memory. - set<string> membersRemovedForStructConstructor; - if (_functionCall.annotation().kind == FunctionCallKind::StructConstructorCall) + // Check for unsupported use of bare static call + if ( + _functionType->kind() == FunctionType::Kind::BareStaticCall && + !m_evmVersion.hasStaticCall() + ) + m_errorReporter.typeError( + _functionCall.location(), + "\"staticcall\" is not supported by the VM version." + ); + + // Check for deprecated function names + if (_functionType->kind() == FunctionType::Kind::KECCAK256) { - TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); - auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); - functionType = structType.constructorType(); - membersRemovedForStructConstructor = structType.membersMissingInMemory(); - _functionCall.annotation().isPure = isPure; + if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + if (functionName->name() == "sha3") + m_errorReporter.typeError( + _functionCall.location(), + "\"sha3\" has been deprecated in favour of \"keccak256\"" + ); } - else if ((functionType = dynamic_pointer_cast<FunctionType const>(expressionType))) - _functionCall.annotation().isPure = - isPure && - _functionCall.expression().annotation().isPure && - functionType->isPure(); - - bool allowDynamicTypes = m_evmVersion.supportsReturndata(); - if (!functionType) + else if (_functionType->kind() == FunctionType::Kind::Selfdestruct) { - m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); - _functionCall.annotation().type = make_shared<TupleType>(); - return false; + if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + if (functionName->name() == "suicide") + m_errorReporter.typeError( + _functionCall.location(), + "\"suicide\" has been deprecated in favour of \"selfdestruct\"" + ); } - if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall()) - m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version."); + // Check for event outside of emit statement + if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event) + m_errorReporter.typeError( + _functionCall.location(), + "Event invocations have to be prefixed by \"emit\"." + ); + + // Perform standard function call type checking + typeCheckFunctionGeneralChecks(_functionCall, _functionType); +} + +void TypeChecker::typeCheckABIEncodeFunctions( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ + solAssert(!!_functionType, ""); + solAssert( + _functionType->kind() == FunctionType::Kind::ABIEncode || + _functionType->kind() == FunctionType::Kind::ABIEncodePacked || + _functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || + _functionType->kind() == FunctionType::Kind::ABIEncodeWithSignature, + "ABI function has unexpected FunctionType::Kind." + ); + solAssert(_functionType->takesArbitraryParameters(), "ABI functions should be variadic."); - if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression())) + bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked; + solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding"); + + bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count( + ExperimentalFeature::ABIEncoderV2 + ); + + // Check for named arguments + if (!_functionCall.names().empty()) { - if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::KECCAK256) - m_errorReporter.typeError(_functionCall.location(), "\"sha3\" has been deprecated in favour of \"keccak256\""); - else if (functionName->name() == "suicide" && functionType->kind() == FunctionType::Kind::Selfdestruct) - m_errorReporter.typeError(_functionCall.location(), "\"suicide\" has been deprecated in favour of \"selfdestruct\""); + m_errorReporter.typeError( + _functionCall.location(), + "Named arguments cannot be used for functions that take arbitrary parameters." + ); + return; } - if (!m_insideEmitStatement && functionType->kind() == FunctionType::Kind::Event) - m_errorReporter.typeError(_functionCall.location(), "Event invocations have to be prefixed by \"emit\"."); - TypePointers parameterTypes = functionType->parameterTypes(); + // Perform standard function call type checking + typeCheckFunctionGeneralChecks(_functionCall, _functionType); - if (!functionType->padArguments()) + // Check additional arguments for variadic functions + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + for (size_t i = 0; i < arguments.size(); ++i) { - for (size_t i = 0; i < arguments.size(); ++i) + auto const& argType = type(*arguments[i]); + + if (argType->category() == Type::Category::RationalNumber) { - auto const& argType = type(*arguments[i]); - if (auto literal = dynamic_cast<RationalNumberType const*>(argType.get())) + if (!argType->mobileType()) { - if (literal->mobileType()) - m_errorReporter.typeError( - arguments[i]->location(), - "Cannot perform packed encoding for a literal. Please convert it to an explicit type first." - ); - else - { - /* If no mobile type is available an error will be raised elsewhere. */ - solAssert(m_errorReporter.hasErrors(), ""); - } + m_errorReporter.typeError( + arguments[i]->location(), + "Invalid rational number (too large or division by zero)." + ); + continue; + } + else if (isPacked) + { + m_errorReporter.typeError( + arguments[i]->location(), + "Cannot perform packed encoding for a literal." + " Please convert it to an explicit type first." + ); + continue; } } + + if (!argType->fullEncodingType(false, abiEncoderV2, !_functionType->padArguments())) + m_errorReporter.typeError( + arguments[i]->location(), + "This type cannot be encoded." + ); } +} - bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); +void TypeChecker::typeCheckFunctionGeneralChecks( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType +) +{ + // Actual function call or struct constructor call. - // Will be assigned to .type at the end (turning multi-elements into a tuple). - TypePointers returnTypes = - allowDynamicTypes ? - functionType->returnParameterTypes() : - functionType->returnParameterTypesWithoutDynamicTypes(); + solAssert(!!_functionType, ""); + solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); - if (functionType->kind() == FunctionType::Kind::ABIDecode) - returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); - else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) - { - solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); - m_errorReporter.typeError( - _functionCall.location(), - "Need at least " + - toString(parameterTypes.size()) + - " arguments for function call, but provided only " + - toString(arguments.size()) + - "." - ); - } - else if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size()) + bool const isPositionalCall = _functionCall.names().empty(); + bool const isVariadic = _functionType->takesArbitraryParameters(); + + solAssert( + !isVariadic || _functionCall.annotation().kind == FunctionCallKind::FunctionCall, + "Struct constructor calls cannot be variadic." + ); + + TypePointers const& parameterTypes = _functionType->parameterTypes(); + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + vector<ASTPointer<ASTString>> const& argumentNames = _functionCall.names(); + + // Check number of passed in arguments + if ( + arguments.size() < parameterTypes.size() || + (!isVariadic && arguments.size() > parameterTypes.size()) + ) { - bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + bool const isStructConstructorCall = + _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall; + + string msg; + + if (isVariadic) + msg += + "Need at least " + + toString(parameterTypes.size()) + + " arguments for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ", but provided only " + + toString(arguments.size()) + + "."; + else + msg += + "Wrong argument count for " + + string(isStructConstructorCall ? "struct constructor" : "function call") + + ": " + + toString(arguments.size()) + + " arguments given but " + + string(isVariadic ? "need at least " : "expected ") + + toString(parameterTypes.size()) + + "."; - string msg = - "Wrong argument count for " + - string(isStructConstructorCall ? "struct constructor" : "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().kind == FunctionCallKind::StructConstructorCall && !membersRemovedForStructConstructor.empty()) + if (isStructConstructorCall) { - msg += " Members that have to be skipped in memory:"; - for (auto const& member: membersRemovedForStructConstructor) - msg += " " + member; + /// For error message: Struct members that were removed during conversion to memory. + TypePointer const expressionType = type(_functionCall.expression()); + TypeType const& t = dynamic_cast<TypeType const&>(*expressionType); + auto const& structType = dynamic_cast<StructType const&>(*t.actualType()); + set<string> membersRemovedForStructConstructor = structType.membersMissingInMemory(); + + if (!membersRemovedForStructConstructor.empty()) + { + msg += " Members that have to be skipped in memory:"; + for (auto const& member: membersRemovedForStructConstructor) + msg += " " + member; + } } else if ( - functionType->kind() == FunctionType::Kind::BareCall || - functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall || - functionType->kind() == FunctionType::Kind::BareStaticCall + _functionType->kind() == FunctionType::Kind::BareCall || + _functionType->kind() == FunctionType::Kind::BareCallCode || + _functionType->kind() == FunctionType::Kind::BareDelegateCall || + _functionType->kind() == FunctionType::Kind::BareStaticCall ) { if (arguments.empty()) - msg += " This function requires a single bytes argument. Use \"\" as argument to provide empty calldata."; + msg += + " This function requires a single bytes argument." + " Use \"\" as argument to provide empty calldata."; else - msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it."; + msg += + " This function requires a single bytes argument." + " If all your arguments are value types, you can use" + " abi.encode(...) to properly generate it."; } else if ( - functionType->kind() == FunctionType::Kind::KECCAK256 || - functionType->kind() == FunctionType::Kind::SHA256 || - functionType->kind() == FunctionType::Kind::RIPEMD160 + _functionType->kind() == FunctionType::Kind::KECCAK256 || + _functionType->kind() == FunctionType::Kind::SHA256 || + _functionType->kind() == FunctionType::Kind::RIPEMD160 ) msg += " This function requires a single bytes argument." - " Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour" - " or abi.encode(...) to use ABI encoding."; + " Use abi.encodePacked(...) to obtain the pre-0.5.0" + " behaviour or abi.encode(...) to use ABI encoding."; m_errorReporter.typeError(_functionCall.location(), msg); + return; } - else if (isPositionalCall) - { - for (size_t i = 0; i < arguments.size(); ++i) - { - auto const& argType = type(*arguments[i]); - if (functionType->takesArbitraryParameters() && i >= parameterTypes.size()) - { - bool errored = false; - if (auto t = dynamic_cast<RationalNumberType const*>(argType.get())) - if (!t->mobileType()) - { - m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); - errored = true; - } - if (!errored && !argType->fullEncodingType(false, abiEncoderV2, !functionType->padArguments())) - m_errorReporter.typeError(arguments[i]->location(), "This type cannot be encoded."); - } - else if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) - { - string msg = - "Invalid type for argument in function call. " - "Invalid implicit conversion from " + - type(*arguments[i])->toString() + - " to " + - parameterTypes[i]->toString() + - " requested."; - if ( - functionType->kind() == FunctionType::Kind::BareCall || - functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall || - functionType->kind() == FunctionType::Kind::BareStaticCall - ) - msg += " This function requires a single bytes argument. If all your arguments are value types, you can use abi.encode(...) to properly generate it."; - else if ( - functionType->kind() == FunctionType::Kind::KECCAK256 || - functionType->kind() == FunctionType::Kind::SHA256 || - functionType->kind() == FunctionType::Kind::RIPEMD160 - ) - msg += - " This function requires a single bytes argument." - " Use abi.encodePacked(...) to obtain the pre-0.5.0 behaviour" - " or abi.encode(...) to use ABI encoding."; - m_errorReporter.typeError(arguments[i]->location(), msg); - } - } - } + + // Parameter to argument map + std::vector<Expression const*> paramArgMap(parameterTypes.size()); + + // Map parameters to arguments - trivially for positional calls, less so for named calls + if (isPositionalCall) + for (size_t i = 0; i < paramArgMap.size(); ++i) + paramArgMap[i] = arguments[i].get(); else { - // call by named arguments - auto const& parameterNames = functionType->parameterNames(); - if (functionType->takesArbitraryParameters()) + auto const& parameterNames = _functionType->parameterNames(); + + // Check for expected number of named arguments + if (parameterNames.size() != argumentNames.size()) + { m_errorReporter.typeError( _functionCall.location(), - "Named arguments cannot be used for functions that take arbitrary parameters." + parameterNames.size() > argumentNames.size() ? + "Some argument names are missing." : + "Too many arguments." ); - else if (parameterNames.size() > argumentNames.size()) - m_errorReporter.typeError(_functionCall.location(), "Some argument names are missing."); - else if (parameterNames.size() < argumentNames.size()) - m_errorReporter.typeError(_functionCall.location(), "Too many arguments."); - else + return; + } + + // Check for duplicate argument names { - // 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; - m_errorReporter.typeError(arguments[i]->location(), "Duplicate named argument."); + m_errorReporter.typeError( + arguments[i]->location(), + "Duplicate named argument \"" + *argumentNames[i] + "\"." + ); } + if (duplication) + return; + } + + // map parameter names to argument names + { + bool not_all_mapped = false; - // check actual types - if (!duplication) - for (size_t i = 0; i < argumentNames.size(); i++) + for (size_t i = 0; i < paramArgMap.size(); i++) + { + size_t j; + for (j = 0; j < argumentNames.size(); j++) + if (parameterNames[i] == *argumentNames[j]) + break; + + if (j < argumentNames.size()) + paramArgMap[i] = arguments[j].get(); + else { - 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])) - m_errorReporter.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) - m_errorReporter.typeError( - _functionCall.location(), - "Named argument \"" + *argumentNames[i] + "\" does not match function declaration." - ); + paramArgMap[i] = nullptr; + not_all_mapped = true; + m_errorReporter.typeError( + _functionCall.location(), + "Named argument \"" + + *argumentNames[i] + + "\" does not match function declaration." + ); } + } + + if (not_all_mapped) + return; } } - if (returnTypes.size() == 1) - _functionCall.annotation().type = returnTypes.front(); - else - _functionCall.annotation().type = make_shared<TupleType>(returnTypes); + // Check for compatible types between arguments and parameters + for (size_t i = 0; i < paramArgMap.size(); ++i) + { + solAssert(!!paramArgMap[i], "unmapped parameter"); + if (!type(*paramArgMap[i])->isImplicitlyConvertibleTo(*parameterTypes[i])) + { + string msg = + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*paramArgMap[i])->toString() + + " to " + + parameterTypes[i]->toString() + + " requested."; + if ( + _functionType->kind() == FunctionType::Kind::BareCall || + _functionType->kind() == FunctionType::Kind::BareCallCode || + _functionType->kind() == FunctionType::Kind::BareDelegateCall || + _functionType->kind() == FunctionType::Kind::BareStaticCall + ) + msg += + " This function requires a single bytes argument." + " If all your arguments are value types, you can" + " use abi.encode(...) to properly generate it."; + else if ( + _functionType->kind() == FunctionType::Kind::KECCAK256 || + _functionType->kind() == FunctionType::Kind::SHA256 || + _functionType->kind() == FunctionType::Kind::RIPEMD160 + ) + msg += + " This function requires a single bytes argument." + " Use abi.encodePacked(...) to obtain the pre-0.5.0" + " behaviour or abi.encode(...) to use ABI encoding."; + m_errorReporter.typeError(paramArgMap[i]->location(), msg); + } + } +} + +bool TypeChecker::visit(FunctionCall const& _functionCall) +{ + vector<ASTPointer<Expression const>> const& arguments = _functionCall.arguments(); + bool argumentsArePure = true; + + // We need to check arguments' type first as they will be needed for overload resolution. + for (ASTPointer<Expression const> const& argument: arguments) + { + argument->accept(*this); + if (!argument->annotation().isPure) + argumentsArePure = false; + } + + // For positional calls only, store argument types + if (_functionCall.names().empty()) + { + shared_ptr<TypePointers> argumentTypes = make_shared<TypePointers>(); + for (ASTPointer<Expression const> const& argument: arguments) + argumentTypes->push_back(type(*argument)); + _functionCall.expression().annotation().argumentTypes = move(argumentTypes); + } + + _functionCall.expression().accept(*this); + + TypePointer const& expressionType = type(_functionCall.expression()); + + // Determine function call kind and function type for this FunctionCall node + FunctionCallAnnotation& funcCallAnno = _functionCall.annotation(); + FunctionTypePointer functionType; + + // Determine and assign function call kind, purity and function type for this FunctionCall node + switch (expressionType->category()) + { + case Type::Category::Function: + functionType = dynamic_pointer_cast<FunctionType const>(expressionType); + funcCallAnno.kind = FunctionCallKind::FunctionCall; + + // Purity for function calls also depends upon the callee and its FunctionType + funcCallAnno.isPure = + argumentsArePure && + _functionCall.expression().annotation().isPure && + functionType && + functionType->isPure(); + + break; + + case Type::Category::TypeType: + { + // Determine type for type conversion or struct construction expressions + TypePointer const& actualType = dynamic_cast<TypeType const&>(*expressionType).actualType(); + solAssert(!!actualType, ""); + + if (actualType->category() == Type::Category::Struct) + { + functionType = dynamic_cast<StructType const&>(*actualType).constructorType(); + funcCallAnno.kind = FunctionCallKind::StructConstructorCall; + funcCallAnno.isPure = argumentsArePure; + } + else + { + funcCallAnno.kind = FunctionCallKind::TypeConversion; + funcCallAnno.isPure = argumentsArePure; + } + + break; + } + + default: + m_errorReporter.typeError(_functionCall.location(), "Type is not callable"); + funcCallAnno.kind = FunctionCallKind::Unset; + funcCallAnno.isPure = argumentsArePure; + break; + } + + // Determine return types + switch (funcCallAnno.kind) + { + case FunctionCallKind::TypeConversion: + funcCallAnno.type = typeCheckTypeConversionAndRetrieveReturnType(_functionCall); + break; + + case FunctionCallKind::StructConstructorCall: // fall-through + case FunctionCallKind::FunctionCall: + { + TypePointers returnTypes; + + switch (functionType->kind()) + { + case FunctionType::Kind::ABIDecode: + { + bool const abiEncoderV2 = + m_scope->sourceUnit().annotation().experimentalFeatures.count( + ExperimentalFeature::ABIEncoderV2 + ); + returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); + break; + } + case FunctionType::Kind::ABIEncode: + case FunctionType::Kind::ABIEncodePacked: + case FunctionType::Kind::ABIEncodeWithSelector: + case FunctionType::Kind::ABIEncodeWithSignature: + { + typeCheckABIEncodeFunctions(_functionCall, functionType); + returnTypes = functionType->returnParameterTypes(); + break; + } + default: + { + typeCheckFunctionCall(_functionCall, functionType); + returnTypes = m_evmVersion.supportsReturndata() ? + functionType->returnParameterTypes() : + functionType->returnParameterTypesWithoutDynamicTypes(); + break; + } + } + + funcCallAnno.type = returnTypes.size() == 1 ? + move(returnTypes.front()) : + make_shared<TupleType>(move(returnTypes)); + + break; + } + + case FunctionCallKind::Unset: // fall-through + default: + // for non-callables, ensure error reported and annotate node to void function + solAssert(m_errorReporter.hasErrors(), ""); + funcCallAnno.kind = FunctionCallKind::FunctionCall; + funcCallAnno.type = make_shared<TupleType>(); + break; + } return false; } diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index 6ea99ca2..c76fa466 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -94,7 +94,33 @@ private: /// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the /// vector of return types (which is basically the second argument) if successful. It returns /// the empty vector on error. - TypePointers typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2); + TypePointers typeCheckABIDecodeAndRetrieveReturnType( + FunctionCall const& _functionCall, + bool _abiEncoderV2 + ); + + /// Performs type checks and determines result types for type conversion FunctionCall nodes. + TypePointer typeCheckTypeConversionAndRetrieveReturnType( + FunctionCall const& _functionCall + ); + + /// Performs type checks on function call and struct ctor FunctionCall nodes (except for kind ABIDecode). + void typeCheckFunctionCall( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); + + /// Performs general number and type checks of arguments against function call and struct ctor FunctionCall node parameters. + void typeCheckFunctionGeneralChecks( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); + + /// Performs general checks and checks specific to ABI encode functions + void typeCheckABIEncodeFunctions( + FunctionCall const& _functionCall, + FunctionTypePointer _functionType + ); virtual void endVisit(InheritanceSpecifier const& _inheritance) override; virtual void endVisit(UsingForDirective const& _usingFor) override; diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol index fab4beff..88402fa3 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/102_duplicate_parameter_names_in_named_args.sol @@ -8,4 +8,4 @@ contract test { } // ---- // Warning: (31-37): This declaration shadows an existing declaration. -// TypeError: (159-160): Duplicate named argument. +// TypeError: (159-160): Duplicate named argument "a". diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol new file mode 100644 index 00000000..6be591f6 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/583_abi_encode_packed_with_rational_number_constant.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { abi.encodePacked(0/1); } +} +// ---- +// TypeError: (61-64): Cannot perform packed encoding for a literal. Please convert it to an explicit type first.
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol new file mode 100644 index 00000000..c95eeb35 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/584_abi_decode_with_tuple_of_other_than_types.sol @@ -0,0 +1,5 @@ +contract C { + function f() pure public { abi.decode("", (0)); } +} +// ---- +// TypeError: (60-61): Argument has to be a type name. |