diff options
author | Anurag Dashputre <anurag4u80@gmail.com> | 2018-08-23 14:26:45 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-23 14:26:45 +0800 |
commit | 8497dcd721ff0a113374c0c1e1778d44265398a6 (patch) | |
tree | 2833ab7b3c7513647c0476d0e5d33dc11fcd6951 /libsolidity/analysis | |
parent | 55524788e2829b3a2b9c6c513f78ba2074aa3385 (diff) | |
parent | 410d288dfc2e08c42df58c7e01ad5c332ce92727 (diff) | |
download | dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.gz dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.bz2 dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.lz dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.xz dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.tar.zst dexon-solidity-8497dcd721ff0a113374c0c1e1778d44265398a6.zip |
Merge branch 'develop' into anurag_issue_3667
Diffstat (limited to 'libsolidity/analysis')
-rw-r--r-- | libsolidity/analysis/ControlFlowAnalyzer.cpp | 5 | ||||
-rw-r--r-- | libsolidity/analysis/DeclarationContainer.cpp | 7 | ||||
-rw-r--r-- | libsolidity/analysis/DocStringAnalyser.cpp | 34 | ||||
-rw-r--r-- | libsolidity/analysis/DocStringAnalyser.h | 11 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.cpp | 11 | ||||
-rw-r--r-- | libsolidity/analysis/NameAndTypeResolver.h | 2 | ||||
-rw-r--r-- | libsolidity/analysis/ReferencesResolver.cpp | 192 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 164 | ||||
-rw-r--r-- | libsolidity/analysis/TypeChecker.h | 8 | ||||
-rw-r--r-- | libsolidity/analysis/ViewPureChecker.cpp | 2 |
10 files changed, 283 insertions, 153 deletions
diff --git a/libsolidity/analysis/ControlFlowAnalyzer.cpp b/libsolidity/analysis/ControlFlowAnalyzer.cpp index 483d08c8..ab6569be 100644 --- a/libsolidity/analysis/ControlFlowAnalyzer.cpp +++ b/libsolidity/analysis/ControlFlowAnalyzer.cpp @@ -75,7 +75,10 @@ void ControlFlowAnalyzer::checkUnassignedStorageReturnValues( { auto& unassignedAtFunctionEntry = unassigned[_functionEntry]; for (auto const& returnParameter: _function.returnParameterList()->parameters()) - if (returnParameter->type()->dataStoredIn(DataLocation::Storage)) + if ( + returnParameter->type()->dataStoredIn(DataLocation::Storage) || + returnParameter->type()->category() == Type::Category::Mapping + ) unassignedAtFunctionEntry.insert(returnParameter.get()); } diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp index 347daaf8..5f980788 100644 --- a/libsolidity/analysis/DeclarationContainer.cpp +++ b/libsolidity/analysis/DeclarationContainer.cpp @@ -138,19 +138,22 @@ vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _n vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const { static size_t const MAXIMUM_EDIT_DISTANCE = 2; + // because the function below has quadratic runtime - it will not magically improve once a better algorithm is discovered ;) + // since 80 is the suggested line length limit, we use 80^2 as length threshold + static size_t const MAXIMUM_LENGTH_THRESHOLD = 80 * 80; vector<ASTString> similar; for (auto const& declaration: m_declarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } for (auto const& declaration: m_invisibleDeclarations) { string const& declarationName = declaration.first; - if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE)) + if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE, MAXIMUM_LENGTH_THRESHOLD)) similar.push_back(declarationName); } diff --git a/libsolidity/analysis/DocStringAnalyser.cpp b/libsolidity/analysis/DocStringAnalyser.cpp index b3fb5258..c1b97def 100644 --- a/libsolidity/analysis/DocStringAnalyser.cpp +++ b/libsolidity/analysis/DocStringAnalyser.cpp @@ -48,7 +48,10 @@ bool DocStringAnalyser::visit(ContractDefinition const& _contract) bool DocStringAnalyser::visit(FunctionDefinition const& _function) { - handleCallable(_function, _function, _function.annotation()); + if (_function.isConstructor()) + handleConstructor(_function, _function, _function.annotation()); + else + handleCallable(_function, _function, _function.annotation()); return true; } @@ -66,15 +69,11 @@ bool DocStringAnalyser::visit(EventDefinition const& _event) return true; } -void DocStringAnalyser::handleCallable( +void DocStringAnalyser::checkParameters( CallableDeclaration const& _callable, - Documented const& _node, DocumentedAnnotation& _annotation ) { - static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"}; - parseDocStrings(_node, _annotation, validTags, "functions"); - set<string> validParams; for (auto const& p: _callable.parameters()) validParams.insert(p->name()); @@ -89,6 +88,29 @@ void DocStringAnalyser::handleCallable( i->second.paramName + "\" not found in the parameter list of the function." ); + +} + +void DocStringAnalyser::handleConstructor( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation +) +{ + static const set<string> validTags = set<string>{"author", "dev", "notice", "param"}; + parseDocStrings(_node, _annotation, validTags, "constructor"); + checkParameters(_callable, _annotation); +} + +void DocStringAnalyser::handleCallable( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation +) +{ + static const set<string> validTags = set<string>{"author", "dev", "notice", "return", "param"}; + parseDocStrings(_node, _annotation, validTags, "functions"); + checkParameters(_callable, _annotation); } void DocStringAnalyser::parseDocStrings( diff --git a/libsolidity/analysis/DocStringAnalyser.h b/libsolidity/analysis/DocStringAnalyser.h index 158b4060..5d339428 100644 --- a/libsolidity/analysis/DocStringAnalyser.h +++ b/libsolidity/analysis/DocStringAnalyser.h @@ -48,6 +48,17 @@ private: virtual bool visit(ModifierDefinition const& _modifier) override; virtual bool visit(EventDefinition const& _event) override; + void checkParameters( + CallableDeclaration const& _callable, + DocumentedAnnotation& _annotation + ); + + void handleConstructor( + CallableDeclaration const& _callable, + Documented const& _node, + DocumentedAnnotation& _annotation + ); + void handleCallable( CallableDeclaration const& _callable, Documented const& _node, diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index c5c429ce..b452a49a 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -626,6 +626,17 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&) closeCurrentScope(); } +bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName) +{ + enterNewSubScope(_funTypeName); + return true; +} + +void DeclarationRegistrationHelper::endVisit(FunctionTypeName&) +{ + closeCurrentScope(); +} + bool DeclarationRegistrationHelper::visit(Block& _block) { _block.setScope(m_currentScope); diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h index 8178ee17..a72c21e3 100644 --- a/libsolidity/analysis/NameAndTypeResolver.h +++ b/libsolidity/analysis/NameAndTypeResolver.h @@ -171,6 +171,8 @@ private: void endVisit(FunctionDefinition& _function) override; bool visit(ModifierDefinition& _modifier) override; void endVisit(ModifierDefinition& _modifier) override; + bool visit(FunctionTypeName& _funTypeName) override; + void endVisit(FunctionTypeName& _funTypeName) override; bool visit(Block& _block) override; void endVisit(Block& _block) override; bool visit(ForStatement& _forLoop) override; diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index fa0888dd..f33de7b7 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -30,7 +30,10 @@ #include <libsolidity/inlineasm/AsmData.h> #include <libsolidity/interface/ErrorReporter.h> +#include <libdevcore/StringUtils.h> + #include <boost/algorithm/string.hpp> +#include <boost/range/adaptor/transformed.hpp> using namespace std; using namespace dev; @@ -155,7 +158,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration)) _typeName.annotation().type = make_shared<ContractType>(*contract); else + { + _typeName.annotation().type = make_shared<TupleType>(); typeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + } } void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) @@ -166,13 +172,13 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) case VariableDeclaration::Visibility::External: break; default: - typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); + fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\"."); return; } if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External) { - typeError(_typeName.location(), "Only external function types can be payable."); + fatalTypeError(_typeName.location(), "Only external function types can be payable."); return; } @@ -182,7 +188,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName) solAssert(t->annotation().type, "Type not set for parameter."); if (!t->annotation().type->canBeUsedExternally(false)) { - typeError(t->location(), "Internal type cannot be used for external function type."); + fatalTypeError(t->location(), "Internal type cannot be used for external function type."); return; } } @@ -300,6 +306,9 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) if (_variable.annotation().type) return; + if (_variable.isConstant() && !_variable.isStateVariable()) + m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables."); + if (!_variable.typeName()) { // This can still happen in very unusual cases where a developer uses constructs, such as @@ -309,127 +318,92 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable) // after this step. return; } - - TypePointer type; - type = _variable.typeName()->annotation().type; using Location = VariableDeclaration::Location; Location varLoc = _variable.referenceLocation(); DataLocation typeLoc = DataLocation::Memory; - // References are forced to calldata for external function parameters (not return) - // and memory for parameters (also return) of publicly visible functions. - // They default to memory for function parameters and storage for local variables. - // As an exception, "storage" is allowed for library functions. - if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) + + set<Location> allowedDataLocations = _variable.allowedDataLocations(); + if (!allowedDataLocations.count(varLoc)) { - bool isPointer = true; - if (_variable.isExternalCallableParameter()) + auto locationToString = [](VariableDeclaration::Location _location) -> string { - auto const& contract = dynamic_cast<ContractDefinition const&>( - *dynamic_cast<Declaration const&>(*_variable.scope()).scope() - ); - if (contract.isLibrary()) - { - if (varLoc == Location::Memory) - fatalTypeError(_variable.location(), - "Location has to be calldata or storage for external " - "library functions (remove the \"memory\" keyword)." - ); - } - else + switch (_location) { - // force location of external function parameters (not return) to calldata - if (varLoc != Location::CallData && varLoc != Location::Default) - fatalTypeError(_variable.location(), - "Location has to be calldata for external functions " - "(remove the \"memory\" or \"storage\" keyword)." - ); + case Location::Memory: return "\"memory\""; + case Location::Storage: return "\"storage\""; + case Location::CallData: return "\"calldata\""; + case Location::Default: return "none"; } - if (varLoc == Location::Default || varLoc == Location::CallData) - typeLoc = DataLocation::CallData; - else - typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; - } - else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic()) + return {}; + }; + + string errorString; + if (!_variable.hasReferenceOrMappingType()) + errorString = "Data location can only be specified for array, struct or mapping types"; + else { - auto const& contract = dynamic_cast<ContractDefinition const&>( - *dynamic_cast<Declaration const&>(*_variable.scope()).scope() + errorString = "Data location must be " + + joinHumanReadable( + allowedDataLocations | boost::adaptors::transformed(locationToString), + ", ", + " or " ); - // force locations of public or external function (return) parameters to memory - if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary()) - fatalTypeError(_variable.location(), - "Location has to be memory for publicly visible functions " - "(remove the \"storage\" or \"calldata\" keyword)." - ); - if (varLoc == Location::Default || !contract.isLibrary()) - typeLoc = DataLocation::Memory; + if (_variable.isCallableParameter()) + errorString += + " for " + + string(_variable.isReturnParameter() ? "return " : "") + + "parameter in" + + string(_variable.isExternalCallableParameter() ? " external" : "") + + " function"; else - { - if (varLoc == Location::CallData) - fatalTypeError(_variable.location(), - "Location cannot be calldata for non-external functions " - "(remove the \"calldata\" keyword)." - ); - typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage; - } + errorString += " for variable"; } - else + errorString += ", but " + locationToString(varLoc) + " was given."; + typeError(_variable.location(), errorString); + + solAssert(!allowedDataLocations.empty(), ""); + varLoc = *allowedDataLocations.begin(); + } + + // Find correct data location. + if (_variable.isEventParameter()) + { + solAssert(varLoc == Location::Default, ""); + typeLoc = DataLocation::Memory; + } + else if (_variable.isStateVariable()) + { + solAssert(varLoc == Location::Default, ""); + typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage; + } + else if ( + dynamic_cast<StructDefinition const*>(_variable.scope()) || + dynamic_cast<EnumDefinition const*>(_variable.scope()) + ) + // The actual location will later be changed depending on how the type is used. + typeLoc = DataLocation::Storage; + else + switch (varLoc) { - if (_variable.isConstant()) - { - if (varLoc != Location::Default && varLoc != Location::Memory) - fatalTypeError( - _variable.location(), - "Data location has to be \"memory\" (or unspecified) for constants." - ); - typeLoc = DataLocation::Memory; - } - else if (varLoc == Location::Default) - { - if (_variable.isCallableParameter()) - typeLoc = DataLocation::Memory; - else - { - typeLoc = DataLocation::Storage; - if (_variable.isLocalVariable()) - typeError( - _variable.location(), - "Data location must be specified as either \"memory\" or \"storage\"." - ); - } - } - else - { - switch (varLoc) - { - case Location::Memory: - typeLoc = DataLocation::Memory; - break; - case Location::Storage: - typeLoc = DataLocation::Storage; - break; - case Location::CallData: - fatalTypeError(_variable.location(), - "Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)." - ); - break; - default: - solAssert(false, "Unknown data location"); - } - } - isPointer = !_variable.isStateVariable(); + case Location::Memory: + typeLoc = DataLocation::Memory; + break; + case Location::Storage: + typeLoc = DataLocation::Storage; + break; + case Location::CallData: + typeLoc = DataLocation::CallData; + break; + case Location::Default: + solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set."); } - type = ref->copyForLocation(typeLoc, isPointer); - } - else if (dynamic_cast<MappingType const*>(type.get())) + + TypePointer type = _variable.typeName()->annotation().type; + if (auto ref = dynamic_cast<ReferenceType const*>(type.get())) { - if (_variable.isLocalVariable() && varLoc != Location::Storage) - typeError( - _variable.location(), - "Data location for mappings must be specified as \"storage\"." - ); + bool isPointer = !_variable.isStateVariable(); + type = ref->copyForLocation(typeLoc, isPointer); } - else if (varLoc != Location::Default && !ref) - typeError(_variable.location(), "Data location can only be given for array or struct types."); _variable.annotation().type = type; } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index bcc3757a..fca64f6a 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -525,6 +525,75 @@ void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment) ); } +TypePointer TypeChecker::typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2) +{ + vector<ASTPointer<Expression const>> arguments = _functionCall.arguments(); + if (arguments.size() != 2) + m_errorReporter.typeError( + _functionCall.location(), + "This function takes two arguments, but " + + toString(arguments.size()) + + " were provided." + ); + if (arguments.size() >= 1 && !type(*arguments.front())->isImplicitlyConvertibleTo(ArrayType(DataLocation::Memory))) + m_errorReporter.typeError( + arguments.front()->location(), + "Invalid type for argument in function call. " + "Invalid implicit conversion from " + + type(*arguments.front())->toString() + + " to bytes memory requested." + ); + + TypePointer returnType = make_shared<TupleType>(); + + if (arguments.size() < 2) + return returnType; + + // The following is a rather syntactic restriction, but we check it here anyway: + // The second argument has to be a tuple expression containing type names. + TupleExpression const* tupleExpression = dynamic_cast<TupleExpression const*>(arguments[1].get()); + if (!tupleExpression) + { + m_errorReporter.typeError( + arguments[1]->location(), + "The second argument to \"abi.decode\" has to be a tuple of types." + ); + return returnType; + } + + vector<TypePointer> components; + for (auto const& typeArgument: tupleExpression->components()) + { + solAssert(typeArgument, ""); + if (TypeType const* argTypeType = dynamic_cast<TypeType const*>(type(*typeArgument).get())) + { + TypePointer actualType = argTypeType->actualType(); + solAssert(actualType, ""); + // We force memory because the parser currently cannot handle + // data locations. Furthermore, storage can be a little dangerous and + // calldata is not really implemented anyway. + actualType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, actualType); + solAssert( + !actualType->dataStoredIn(DataLocation::CallData) && + !actualType->dataStoredIn(DataLocation::Storage), + "" + ); + if (!actualType->fullEncodingType(false, _abiEncoderV2, false)) + m_errorReporter.typeError( + typeArgument->location(), + "Decoding type " + actualType->toString(false) + " not supported." + ); + components.push_back(actualType); + } + else + { + m_errorReporter.typeError(typeArgument->location(), "Argument has to be a type name."); + components.push_back(make_shared<TupleType>()); + } + } + return make_shared<TupleType>(components); +} + void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast<ContractDefinition const*>(&dereference(_inheritance.name())); @@ -580,9 +649,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor) bool TypeChecker::visit(StructDefinition const& _struct) { - if (m_scope->contractKind() == ContractDefinition::ContractKind::Interface) - m_errorReporter.typeError(_struct.location(), "Structs cannot be defined in interfaces."); - for (ASTPointer<VariableDeclaration> const& member: _struct.members()) if (!type(*member)->canBeStored()) m_errorReporter.typeError(member->location(), "Type cannot be used in struct."); @@ -610,7 +676,10 @@ bool TypeChecker::visit(StructDefinition const& _struct) if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr) m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition."); + bool insideStruct = true; + swap(insideStruct, m_insideStruct); ASTNode::listAccept(_struct.members(), *this); + m_insideStruct = insideStruct; return false; } @@ -629,7 +698,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) } for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) { - if (!type(*var)->canLiveOutsideStorage()) + if ( + type(*var)->category() == Type::Category::Mapping && + !type(*var)->dataStoredIn(DataLocation::Storage) + ) + m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\"."); + else if ( + !type(*var)->canLiveOutsideStorage() && + _function.visibility() > FunctionDefinition::Visibility::Internal + ) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction))) m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions."); @@ -690,10 +767,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(VariableDeclaration const& _variable) { // Forbid any variable declarations inside interfaces unless they are part of - // a function's input/output parameters. + // * a function's input/output parameters, + // * or inside of a struct definition. if ( m_scope->contractKind() == ContractDefinition::ContractKind::Interface && !_variable.isCallableParameter() + && !m_insideStruct ) m_errorReporter.typeError(_variable.location(), "Variables cannot be declared in interfaces."); @@ -711,8 +790,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) expectType(*_variable.value(), *varType); if (_variable.isConstant()) { - if (!_variable.isStateVariable()) - m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier."); if (!_variable.type()->isValueType()) { bool allowed = false; @@ -742,7 +819,9 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ) m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); - if (varType->category() == Type::Category::Array) + switch (varType->category()) + { + case Type::Category::Array: if (auto arrayType = dynamic_cast<ArrayType const*>(varType.get())) if ( ((arrayType->location() == DataLocation::Memory) || @@ -750,6 +829,18 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) !arrayType->validForCalldata() ) m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); + break; + case Type::Category::Mapping: + if (auto mappingType = dynamic_cast<MappingType const*>(varType.get())) + if ( + mappingType->keyType()->isDynamicallySized() && + _variable.visibility() == Declaration::Visibility::Public + ) + m_errorReporter.typeError(_variable.location(), "Dynamically-sized keys for public mappings are not supported."); + break; + default: + break; + } return false; } @@ -818,7 +909,17 @@ bool TypeChecker::visit(EventDefinition const& _eventDef) for (ASTPointer<VariableDeclaration> const& var: _eventDef.parameters()) { if (var->isIndexed()) + { numIndexed++; + if ( + _eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) && + dynamic_cast<ReferenceType const*>(type(*var).get()) + ) + m_errorReporter.typeError( + var->location(), + "Indexed reference types cannot yet be used with ABIEncoderV2." + ); + } if (!type(*var)->canLiveOutsideStorage()) m_errorReporter.typeError(var->location(), "Type is required to live outside storage."); if (!type(*var)->interfaceType(false)) @@ -1282,7 +1383,8 @@ void TypeChecker::endVisit(ExpressionStatement const& _statement) if ( kind == FunctionType::Kind::BareCall || kind == FunctionType::Kind::BareCallCode || - kind == FunctionType::Kind::BareDelegateCall + kind == FunctionType::Kind::BareDelegateCall || + kind == FunctionType::Kind::BareStaticCall ) m_errorReporter.warning(_statement.location(), "Return value of low-level calls not used."); else if (kind == FunctionType::Kind::Send) @@ -1626,10 +1728,13 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) 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())) - // do not change the data location when converting - // (data location cannot yet be specified for type conversions) - resultType = ReferenceType::copyForLocationIfReference(argRefType->location(), resultType); + dataLoc = argRefType->location(); + resultType = ReferenceType::copyForLocationIfReference(dataLoc, resultType); if (!argType->isExplicitlyConvertibleTo(*resultType)) m_errorReporter.typeError( _functionCall.location(), @@ -1674,6 +1779,9 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) return false; } + if (functionType->kind() == FunctionType::Kind::BareStaticCall && !m_evmVersion.hasStaticCall()) + m_errorReporter.typeError(_functionCall.location(), "\"staticcall\" is not supported by the VM version."); + auto returnTypes = allowDynamicTypes ? functionType->returnParameterTypes() : @@ -1716,7 +1824,11 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } } - if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) + bool const abiEncoderV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); + + if (functionType->kind() == FunctionType::Kind::ABIDecode) + _functionCall.annotation().type = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2); + else if (functionType->takesArbitraryParameters() && arguments.size() < parameterTypes.size()) { solAssert(_functionCall.annotation().kind == FunctionCallKind::FunctionCall, ""); m_errorReporter.typeError( @@ -1750,7 +1862,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) else if ( functionType->kind() == FunctionType::Kind::BareCall || functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall + functionType->kind() == FunctionType::Kind::BareDelegateCall || + functionType->kind() == FunctionType::Kind::BareStaticCall ) { if (arguments.empty()) @@ -1771,8 +1884,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) } else if (isPositionalCall) { - bool const abiEncodeV2 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2); - for (size_t i = 0; i < arguments.size(); ++i) { auto const& argType = type(*arguments[i]); @@ -1785,7 +1896,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) m_errorReporter.typeError(arguments[i]->location(), "Invalid rational number (too large or division by zero)."); errored = true; } - if (!errored && !argType->fullEncodingType(false, abiEncodeV2, !functionType->padArguments())) + 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])) @@ -1800,7 +1911,8 @@ bool TypeChecker::visit(FunctionCall const& _functionCall) if ( functionType->kind() == FunctionType::Kind::BareCall || functionType->kind() == FunctionType::Kind::BareCallCode || - functionType->kind() == FunctionType::Kind::BareDelegateCall + 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 ( @@ -2348,22 +2460,6 @@ void TypeChecker::expectType(Expression const& _expression, Type const& _expecte "." ); } - - if ( - type(_expression)->category() == Type::Category::RationalNumber && - _expectedType.category() == Type::Category::FixedBytes - ) - { - auto literal = dynamic_cast<Literal const*>(&_expression); - - if (literal && !literal->isHexNumber()) - m_errorReporter.warning( - _expression.location(), - "Decimal literal assigned to bytesXX variable will be left-aligned. " - "Use an explicit conversion to silence this warning." - ); - } - } void TypeChecker::requireLValue(Expression const& _expression) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index b696de85..4be0d1e4 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -91,6 +91,11 @@ private: // and reports an error, if not. void checkExpressionAssignment(Type const& _type, Expression const& _expression); + /// Performs type checks for ``abi.decode(bytes memory, (...))`` and returns the + /// return type (which is basically the second argument) if successful. It returns + /// the empty tuple type or error. + TypePointer typeCheckABIDecodeAndRetrieveReturnType(FunctionCall const& _functionCall, bool _abiEncoderV2); + virtual void endVisit(InheritanceSpecifier const& _inheritance) override; virtual void endVisit(UsingForDirective const& _usingFor) override; virtual bool visit(StructDefinition const& _struct) override; @@ -149,6 +154,9 @@ private: /// Flag indicating whether we are currently inside an EmitStatement. bool m_insideEmitStatement = false; + /// Flag indicating whether we are currently inside a StructDefinition. + bool m_insideStruct = false; + ErrorReporter& m_errorReporter; }; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index d936ada0..e92ad2fa 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -295,7 +295,7 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) { // we can ignore the kind of magic and only look at the name of the member set<string> static const pureMembers{ - "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "data", "sig", "blockhash" + "encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode", "data", "sig", "blockhash" }; if (!pureMembers.count(member)) mutability = StateMutability::View; |