From 9324fb4f20a848609ea417299869ece0044c44f5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 4 May 2020 18:38:00 +0200 Subject: [PATCH] Free functions. --- libsolidity/analysis/GlobalContext.cpp | 17 +++- libsolidity/analysis/GlobalContext.h | 1 + libsolidity/analysis/NameAndTypeResolver.cpp | 10 ++ libsolidity/analysis/StaticAnalyzer.cpp | 1 + libsolidity/analysis/SyntaxChecker.cpp | 30 +++++- libsolidity/analysis/SyntaxChecker.h | 3 +- libsolidity/analysis/TypeChecker.cpp | 95 +++++++++++++------ libsolidity/analysis/TypeChecker.h | 10 ++ libsolidity/analysis/ViewPureChecker.cpp | 36 +++---- libsolidity/analysis/ViewPureChecker.h | 1 + libsolidity/ast/AST.cpp | 11 ++- libsolidity/ast/AST.h | 8 ++ libsolidity/ast/ASTAnnotations.h | 3 - libsolidity/ast/ASTJsonConverter.cpp | 2 +- libsolidity/ast/ASTJsonImporter.cpp | 16 +++- libsolidity/ast/Types.cpp | 16 ++-- libsolidity/codegen/ExpressionCompiler.cpp | 10 +- libsolidity/parsing/Parser.cpp | 8 +- libsolidity/parsing/Parser.h | 2 +- .../semanticTests/freeFunctions/easy.sol | 13 +++ .../freeFunctions/free_runtimecode.sol | 15 +++ .../semanticTests/freeFunctions/import.sol | 18 ++++ .../freeFunctions/libraries_from_free.sol | 21 ++++ .../freeFunctions/new_operator.sol | 15 +++ .../semanticTests/freeFunctions/overloads.sol | 16 ++++ .../semanticTests/freeFunctions/recursion.sol | 23 +++++ .../freeFunctions/storage_calldata_refs.sol | 17 ++++ .../free_call_via_contract_type.sol | 8 ++ .../freeFunctions/free_constructor.sol | 3 + .../freeFunctions/free_fallback.sol | 3 + .../freeFunctions/free_function_modifier.sol | 4 + .../free_function_qualified_modifier.sol | 9 ++ .../freeFunctions/free_function_shadowing.sol | 9 ++ .../free_function_visibility.sol | 5 + .../free_function_without_body.sol | 3 + .../freeFunctions/free_functions.sol | 4 + .../freeFunctions/free_mutability.sol | 8 ++ .../freeFunctions/free_overload.sol | 9 ++ .../freeFunctions/free_override.sol | 4 + .../freeFunctions/free_payable.sol | 4 + .../freeFunctions/free_receive.sol | 3 + .../freeFunctions/free_storage.sol | 4 + .../freeFunctions/free_virtual.sol | 4 + .../function_same_name_as_contract.sol | 4 + .../function_using_struct_after_contract.sol | 6 ++ .../syntaxTests/freeFunctions/gas_value.sol | 6 ++ .../freeFunctions/qualified_struct_access.sol | 7 ++ .../freeFunctions/struct_after_function.sol | 3 + .../freeFunctions/super_in_free_function.sol | 6 ++ .../freeFunctions/this_in_free_function.sol | 6 ++ .../imports/importing_free_functions.sol | 14 +++ 51 files changed, 481 insertions(+), 73 deletions(-) create mode 100644 test/libsolidity/semanticTests/freeFunctions/easy.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/import.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/new_operator.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/overloads.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/recursion.sol create mode 100644 test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_functions.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_overload.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_override.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_payable.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_receive.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_storage.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/gas_value.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol create mode 100644 test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol create mode 100644 test/libsolidity/syntaxTests/imports/importing_free_functions.sol diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 885ae49a7..dee7e8220 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -134,15 +134,26 @@ vector GlobalContext::declarations() const MagicVariableDeclaration const* GlobalContext::currentThis() const { if (!m_thisPointer[m_currentContract]) - m_thisPointer[m_currentContract] = make_shared(magicVariableToID("this"), "this", TypeProvider::contract(*m_currentContract)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract); + m_thisPointer[m_currentContract] = + make_shared(magicVariableToID("this"), "this", type); + } return m_thisPointer[m_currentContract].get(); - } MagicVariableDeclaration const* GlobalContext::currentSuper() const { if (!m_superPointer[m_currentContract]) - m_superPointer[m_currentContract] = make_shared(magicVariableToID("super"), "super", TypeProvider::contract(*m_currentContract, true)); + { + Type const* type = TypeProvider::emptyTuple(); + if (m_currentContract) + type = TypeProvider::contract(*m_currentContract, true); + m_superPointer[m_currentContract] = + make_shared(magicVariableToID("super"), "super", type); + } return m_superPointer[m_currentContract].get(); } diff --git a/libsolidity/analysis/GlobalContext.h b/libsolidity/analysis/GlobalContext.h index b84e3a026..191359c60 100644 --- a/libsolidity/analysis/GlobalContext.h +++ b/libsolidity/analysis/GlobalContext.h @@ -46,6 +46,7 @@ class GlobalContext: private boost::noncopyable public: GlobalContext(); void setCurrentContract(ContractDefinition const& _contract); + void resetCurrentContract() { m_currentContract = nullptr; } MagicVariableDeclaration const* currentThis() const; MagicVariableDeclaration const* currentSuper() const; diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp index c7be364a3..957046810 100644 --- a/libsolidity/analysis/NameAndTypeResolver.cpp +++ b/libsolidity/analysis/NameAndTypeResolver.cpp @@ -275,6 +275,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res if (!resolveNamesAndTypesInternal(*node, true)) success = false; } + + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); + return success; } else @@ -548,6 +554,10 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) void DeclarationRegistrationHelper::endVisit(ContractDefinition&) { + // make "this" and "super" invisible. + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true); + m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true); + m_globalContext.resetCurrentContract(); m_currentContract = nullptr; closeCurrentScope(); } diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index cd932cf6d..a6d797910 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -311,6 +311,7 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) ); } if ( + m_currentContract && m_currentContract->isLibrary() && functionType->kind() == FunctionType::Kind::DelegateCall && functionType->declaration().scope() == m_currentContract diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index c0380ffe5..a47d522d9 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -295,7 +295,7 @@ bool SyntaxChecker::visit(PlaceholderStatement const&) bool SyntaxChecker::visit(ContractDefinition const& _contract) { - m_isInterface = _contract.isInterface(); + m_currentContractKind = _contract.contractKind(); ASTString const& contractName = _contract.name(); for (FunctionDefinition const* function: _contract.definedFunctions()) @@ -309,19 +309,41 @@ bool SyntaxChecker::visit(ContractDefinition const& _contract) return true; } +void SyntaxChecker::endVisit(ContractDefinition const&) +{ + m_currentContractKind = std::nullopt; +} + bool SyntaxChecker::visit(FunctionDefinition const& _function) { - if (!_function.isConstructor() && _function.noVisibilitySpecified()) + solAssert(_function.isFree() == (m_currentContractKind == std::nullopt), ""); + + if (!_function.isFree() && !_function.isConstructor() && _function.noVisibilitySpecified()) { - string suggestedVisibility = _function.isFallback() || _function.isReceive() || m_isInterface ? "external" : "public"; + string suggestedVisibility = + _function.isFallback() || + _function.isReceive() || + m_currentContractKind == ContractKind::Interface + ? "external" : "public"; m_errorReporter.syntaxError( 4937_error, _function.location(), "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?" ); } + else if (_function.isFree()) + { + if (!_function.noVisibilitySpecified()) + m_errorReporter.syntaxError( + 4126_error, + _function.location(), + "Free functions cannot have visibility." + ); + if (!_function.isImplemented()) + m_errorReporter.typeError(4668_error, _function.location(), "Free functions must be implemented."); + } - if (m_isInterface && !_function.modifiers().empty()) + if (m_currentContractKind == ContractKind::Interface && !_function.modifiers().empty()) m_errorReporter.syntaxError(5842_error, _function.location(), "Functions in interfaces cannot have modifiers."); else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(2668_error, _function.location(), "Functions without implementation cannot have modifiers."); diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h index 1ace2f36a..a5b786e34 100644 --- a/libsolidity/analysis/SyntaxChecker.h +++ b/libsolidity/analysis/SyntaxChecker.h @@ -83,6 +83,7 @@ private: bool visit(PlaceholderStatement const& _placeholderStatement) override; bool visit(ContractDefinition const& _contract) override; + void endVisit(ContractDefinition const& _contract) override; bool visit(FunctionDefinition const& _function) override; bool visit(FunctionTypeName const& _node) override; @@ -102,7 +103,7 @@ private: bool m_versionPragmaFound = false; int m_inLoopDepth = 0; - bool m_isInterface = false; + std::optional m_currentContractKind; SourceUnit const* m_sourceUnit = nullptr; }; diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8ebc76291..28f2b4152 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -271,6 +271,7 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance) { auto base = dynamic_cast(&dereference(_inheritance.name())); solAssert(base, "Base contract not available."); + solAssert(m_currentContract, ""); if (m_currentContract->isInterface() && !base->isInterface()) m_errorReporter.typeError(6536_error, _inheritance.location(), "Interfaces can only inherit from other interfaces."); @@ -327,7 +328,9 @@ bool TypeChecker::visit(FunctionDefinition const& _function) { if (_function.markedVirtual()) { - if (_function.isConstructor()) + if (_function.isFree()) + m_errorReporter.syntaxError(4493_error, _function.location(), "Free functions cannot be virtual."); + else if (_function.isConstructor()) m_errorReporter.typeError(7001_error, _function.location(), "Constructors cannot be virtual."); else if (_function.annotation().contract->isInterface()) m_errorReporter.warning(5815_error, _function.location(), "Interface functions are implicitly \"virtual\""); @@ -336,12 +339,16 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(7801_error, _function.location(), "Library functions cannot be \"virtual\"."); } + if (_function.overrides() && _function.isFree()) + m_errorReporter.syntaxError(1750_error, _function.location(), "Free functions cannot override."); if (_function.isPayable()) { if (_function.libraryFunction()) m_errorReporter.typeError(7708_error, _function.location(), "Library functions cannot be payable."); - if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) + else if (_function.isFree()) + m_errorReporter.typeError(9559_error, _function.location(), "Free functions cannot be payable."); + else if (_function.isOrdinary() && !_function.isPartOfExternalInterface()) m_errorReporter.typeError(5587_error, _function.location(), "\"internal\" and \"private\" functions cannot be payable."); } @@ -415,9 +422,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function) set modifiers; for (ASTPointer const& modifier: _function.modifiers()) { - auto baseContracts = dynamic_cast(*_function.scope()).annotation().linearizedBaseContracts; - // Delete first base which is just the main contract itself - baseContracts.erase(baseContracts.begin()); + vector baseContracts; + if (auto contract = dynamic_cast(_function.scope())) + { + baseContracts = contract->annotation().linearizedBaseContracts; + // Delete first base which is just the main contract itself + baseContracts.erase(baseContracts.begin()); + } visitManually( *modifier, @@ -432,7 +443,15 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else modifiers.insert(decl); } - if (m_currentContract->isInterface()) + + solAssert(_function.isFree() == !m_currentContract, ""); + if (!m_currentContract) + { + solAssert(!_function.isConstructor(), ""); + solAssert(!_function.isFallback(), ""); + solAssert(!_function.isReceive(), ""); + } + else if (m_currentContract->isInterface()) { if (_function.isImplemented()) m_errorReporter.typeError(4726_error, _function.location(), "Functions in interfaces cannot have an implementation."); @@ -445,6 +464,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (m_currentContract->contractKind() == ContractKind::Library) if (_function.isConstructor()) m_errorReporter.typeError(7634_error, _function.location(), "Constructor cannot be defined in libraries."); + if (_function.isImplemented()) _function.body().accept(*this); else if (_function.isConstructor()) @@ -452,7 +472,12 @@ bool TypeChecker::visit(FunctionDefinition const& _function) else if (_function.libraryFunction()) m_errorReporter.typeError(9231_error, _function.location(), "Library functions must be implemented if declared."); else if (!_function.virtualSemantics()) - m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + { + if (_function.isFree()) + solAssert(m_errorReporter.hasErrors(), ""); + else + m_errorReporter.typeError(5424_error, _function.location(), "Functions without implementation must be marked virtual."); + } if (_function.isFallback()) @@ -1726,7 +1751,9 @@ void TypeChecker::typeCheckFunctionCall( if (_functionType->kind() == FunctionType::Kind::Declaration) { + solAssert(_functionType->declaration().annotation().contract, ""); if ( + m_currentContract && m_currentContract->derivesFrom(*_functionType->declaration().annotation().contract) && !dynamic_cast(_functionType->declaration()).isImplemented() ) @@ -2454,18 +2481,23 @@ void TypeChecker::endVisit(NewExpression const& _newExpression) if (contract->abstract()) m_errorReporter.typeError(4614_error, _newExpression.location(), "Cannot instantiate an abstract contract."); - solAssert(!!m_currentContract, ""); - m_currentContract->annotation().contractDependencies.insert(contract); - solAssert( - !contract->annotation().linearizedBaseContracts.empty(), - "Linearized base contracts not yet available." - ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4579_error, - _newExpression.location(), - "Circular reference for contract creation (cannot create instance of derived or same contract)." + if (m_currentContract) + { + // TODO this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + m_currentContract->annotation().contractDependencies.insert(contract); + solAssert( + !contract->annotation().linearizedBaseContracts.empty(), + "Linearized base contracts not yet available." ); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4579_error, + _newExpression.location(), + "Circular reference for contract creation (cannot create instance of derived or same contract)." + ); + } _newExpression.annotation().type = FunctionType::newExpressionType(*contract); } @@ -2507,7 +2539,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) // Retrieve the types of the arguments if this is used to call a function. auto const& arguments = _memberAccess.annotation().arguments; - MemberList::MemberMap possibleMembers = exprType->members(m_currentContract).membersByName(memberName); + MemberList::MemberMap possibleMembers = exprType->members(currentDefinitionScope()).membersByName(memberName); size_t const initialMemberCount = possibleMembers.size(); if (initialMemberCount > 1 && arguments) { @@ -2533,7 +2565,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) DataLocation::Storage, exprType ); - if (!storageType->members(m_currentContract).membersByName(memberName).empty()) + if (!storageType->members(currentDefinitionScope()).membersByName(memberName).empty()) m_errorReporter.fatalTypeError( 4994_error, _memberAccess.location(), @@ -2691,8 +2723,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) { annotation.isPure = true; ContractType const& accessedContractType = dynamic_cast(*magicType->typeArgument()); - m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); - if ( memberName == "runtimeCode" && !accessedContractType.immutableVariables().empty() @@ -2703,12 +2733,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) "\"runtimeCode\" is not available for contracts containing immutable variables." ); - if (contractDependenciesAreCyclic(*m_currentContract)) - m_errorReporter.typeError( - 4224_error, - _memberAccess.location(), - "Circular reference for contract code access." - ); + if (m_currentContract) + { + // TODO in the same way as with ``new``, + // this is not properly detecting creation-cycles if they go through + // internal library functions or free functions. It will be caught at + // code generation time, but it would of course be better to catch it here. + + m_currentContract->annotation().contractDependencies.insert(&accessedContractType.contractDefinition()); + if (contractDependenciesAreCyclic(*m_currentContract)) + m_errorReporter.typeError( + 4224_error, + _memberAccess.location(), + "Circular reference for contract code access." + ); + } } else if (magicType->kind() == MagicType::Kind::MetaType && memberName == "name") annotation.isPure = true; diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index e34c9e9d0..87ae1d83c 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -167,6 +167,16 @@ private: bool experimentalFeatureActive(ExperimentalFeature _feature) const; + /// @returns the current scope that can have function or type definitions. + /// This is either a contract or a source unit. + ASTNode const* currentDefinitionScope() const + { + if (m_currentContract) + return m_currentContract; + else + return m_currentSourceUnit; + } + SourceUnit const* m_currentSourceUnit = nullptr; ContractDefinition const* m_currentContract = nullptr; diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index a1bf31d77..1bf6028e5 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -128,30 +128,23 @@ private: bool ViewPureChecker::check() { - vector contracts; + // Process modifiers first to infer their state mutability. + m_checkModifiers = true; + for (auto const& source: m_ast) + source->accept(*this); - for (auto const& node: m_ast) - { - SourceUnit const* source = dynamic_cast(node.get()); - solAssert(source, ""); - contracts += source->filteredNodes(source->nodes()); - } - - // Check modifiers first to infer their state mutability. - for (auto const& contract: contracts) - for (ModifierDefinition const* mod: contract->functionModifiers()) - mod->accept(*this); - - for (auto const& contract: contracts) - contract->accept(*this); + m_checkModifiers = false; + for (auto const& source: m_ast) + source->accept(*this); return !m_errors; } - - bool ViewPureChecker::visit(FunctionDefinition const& _funDef) { + if (m_checkModifiers) + return false; + solAssert(!m_currentFunction, ""); m_currentFunction = &_funDef; m_bestMutabilityAndLocation = {StateMutability::Pure, _funDef.location()}; @@ -160,6 +153,9 @@ bool ViewPureChecker::visit(FunctionDefinition const& _funDef) void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) { + if (m_checkModifiers) + return; + solAssert(m_currentFunction == &_funDef, ""); if ( m_bestMutabilityAndLocation.mutability < _funDef.stateMutability() && @@ -181,6 +177,9 @@ void ViewPureChecker::endVisit(FunctionDefinition const& _funDef) bool ViewPureChecker::visit(ModifierDefinition const& _modifier) { + if (!m_checkModifiers) + return false; + solAssert(m_currentFunction == nullptr, ""); m_bestMutabilityAndLocation = {StateMutability::Pure, _modifier.location()}; return true; @@ -188,6 +187,9 @@ bool ViewPureChecker::visit(ModifierDefinition const& _modifier) void ViewPureChecker::endVisit(ModifierDefinition const& _modifierDef) { + if (!m_checkModifiers) + return; + solAssert(m_currentFunction == nullptr, ""); m_inferredMutability[&_modifierDef] = std::move(m_bestMutabilityAndLocation); } diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h index 87135b4f7..62444e086 100644 --- a/libsolidity/analysis/ViewPureChecker.h +++ b/libsolidity/analysis/ViewPureChecker.h @@ -75,6 +75,7 @@ private: langutil::ErrorReporter& m_errorReporter; bool m_errors = false; + bool m_checkModifiers = false; MutabilityAndLocation m_bestMutabilityAndLocation = MutabilityAndLocation{StateMutability::Payable, langutil::SourceLocation()}; FunctionDefinition const* m_currentFunction = nullptr; std::map m_inferredMutability; diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index 662ea81d3..621fb3936 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -338,7 +338,7 @@ TypePointer FunctionDefinition::type() const TypePointer FunctionDefinition::typeViaContractName() const { - if (annotation().contract->isLibrary()) + if (libraryFunction()) { if (isPublic()) return FunctionType(*this).asExternallyCallableFunction(true); @@ -374,7 +374,9 @@ FunctionDefinition const& FunctionDefinition::resolveVirtual( if (_searchStart == nullptr && !virtualSemantics()) return *this; - solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + solAssert(!isFree(), ""); + solAssert(isOrdinary(), ""); + solAssert(!libraryFunction(), ""); FunctionType const* functionType = TypeProvider::function(*this)->asExternallyCallableFunction(false); @@ -603,9 +605,8 @@ bool VariableDeclaration::isLibraryFunctionParameter() const if (!isCallableOrCatchParameter()) return false; if (auto const* funDef = dynamic_cast(scope())) - return dynamic_cast(*funDef->scope()).isLibrary(); - else - return false; + return funDef->libraryFunction(); + return false; } bool VariableDeclaration::isEventParameter() const diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 28d71c2b7..8b4025a45 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -774,6 +774,7 @@ public: ASTPointer const& _name, Visibility _visibility, StateMutability _stateMutability, + bool _free, Token _kind, bool _isVirtual, ASTPointer const& _overrides, @@ -787,6 +788,7 @@ public: StructurallyDocumented(_documentation), ImplementationOptional(_body != nullptr), m_stateMutability(_stateMutability), + m_free(_free), m_kind(_kind), m_functionModifiers(std::move(_modifiers)), m_body(_body) @@ -804,6 +806,7 @@ public: bool isConstructor() const { return m_kind == Token::Constructor; } bool isFallback() const { return m_kind == Token::Fallback; } bool isReceive() const { return m_kind == Token::Receive; } + bool isFree() const { return m_free; } Token kind() const { return m_kind; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; } std::vector> const& modifiers() const { return m_functionModifiers; } @@ -815,6 +818,7 @@ public: } bool isVisibleViaContractTypeAccess() const override { + solAssert(!isFree(), ""); return isOrdinary() && visibility() >= Visibility::Public; } bool isPartOfExternalInterface() const override { return isOrdinary() && isPublic(); } @@ -848,8 +852,12 @@ public: ContractDefinition const* _searchStart = nullptr ) const override; +protected: + Visibility defaultVisibility() const override { return isFree() ? Visibility::Internal : Visibility::Public; } + private: StateMutability m_stateMutability; + bool m_free; Token const m_kind; std::vector> m_functionModifiers; ASTPointer m_body; diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 1f6e039e3..2aac79837 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -236,9 +236,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation { /// Referenced declaration, set during reference resolution stage. Declaration const* referencedDeclaration = nullptr; - /// Stores a reference to the current contract. - /// This is needed because types of base contracts change depending on the context. - ContractDefinition const* contractScope = nullptr; }; struct ExpressionAnnotation: ASTAnnotation diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index 064068005..15ac0c620 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -361,6 +361,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node) make_pair("name", _node.name()), make_pair("documentation", _node.documentation() ? toJson(*_node.documentation()) : Json::nullValue), make_pair("kind", TokenTraits::toString(_node.kind())), + make_pair("freeFunction", _node.isFree()), make_pair("stateMutability", stateMutabilityToString(_node.stateMutability())), make_pair("visibility", Declaration::visibilityToString(visibility)), make_pair("virtual", _node.markedVirtual()), @@ -467,7 +468,6 @@ bool ASTJsonConverter::visit(UserDefinedTypeName const& _node) setJsonNode(_node, "UserDefinedTypeName", { make_pair("name", namePathToString(_node.namePath())), make_pair("referencedDeclaration", idOrNull(_node.annotation().referencedDeclaration)), - make_pair("contractScope", idOrNull(_node.annotation().contractScope)), make_pair("typeDescriptions", typePointerToJson(_node.annotation().type, true)) }); return false; diff --git a/libsolidity/ast/ASTJsonImporter.cpp b/libsolidity/ast/ASTJsonImporter.cpp index 2d1fda322..8bc657abf 100644 --- a/libsolidity/ast/ASTJsonImporter.cpp +++ b/libsolidity/ast/ASTJsonImporter.cpp @@ -397,11 +397,25 @@ ASTPointer ASTJsonImporter::createFunctionDefinition(Json::V std::vector> modifiers; for (auto& mod: member(_node, "modifiers")) modifiers.push_back(createModifierInvocation(mod)); + + Visibility vis = Visibility::Default; + bool const freeFunction = _node.isMember("freeFunction") ? _node["freeFunction"].asBool() : false; + if (kind == Token::Constructor) + { + } + else if (freeFunction) + astAssert( + vis == Visibility::Internal || vis == Visibility::Default, + "Expected internal or default visibility for free function." + ); + else + vis = visibility(_node); return createASTNode( _node, memberAsASTString(_node, "name"), - kind == Token::Constructor ? Visibility::Default : visibility(_node), + vis, stateMutability(_node), + freeFunction, kind, memberAsBool(_node, "virtual"), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index a87ae4c3f..956412425 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -3151,10 +3151,8 @@ string FunctionType::toString(bool _short) const { auto const* functionDefinition = dynamic_cast(m_declaration); solAssert(functionDefinition, ""); - auto const* contract = dynamic_cast(functionDefinition->scope()); - solAssert(contract, ""); - name += contract->annotation().canonicalName; - name += '.'; + if (auto const* contract = dynamic_cast(functionDefinition->scope())) + name += contract->annotation().canonicalName + "."; name += functionDefinition->name(); } name += '('; @@ -3275,7 +3273,10 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const { // Note that m_declaration might also be a state variable! solAssert(m_declaration, "Declaration needed to determine interface function type."); - bool isLibraryFunction = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool isLibraryFunction = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + isLibraryFunction = contract->isLibrary(); util::Result paramTypes = transformParametersToExternal(m_parameterTypes, isLibraryFunction); @@ -3569,7 +3570,10 @@ string FunctionType::externalSignature() const } // "inLibrary" is only relevant if this is not an event. - bool const inLibrary = kind() != Kind::Event && dynamic_cast(*m_declaration->scope()).isLibrary(); + bool inLibrary = false; + if (kind() != Kind::Event) + if (auto const* contract = dynamic_cast(m_declaration->scope())) + inLibrary = contract->isLibrary(); auto extParams = transformParametersToExternal(m_parameterTypes, inLibrary); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 8b834031f..d4f70691f 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1736,9 +1736,17 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) Type::Category category = _memberAccess.annotation().type->category(); solAssert( category == Type::Category::TypeType || - category == Type::Category::Module, + category == Type::Category::Module || + category == Type::Category::Function, "" ); + if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + { + auto const* funDef = dynamic_cast(_memberAccess.annotation().referencedDeclaration); + solAssert(funDef && funDef->isFree(), ""); + solAssert(funType->kind() == FunctionType::Kind::Internal, ""); + utils().pushCombinedFunctionEntryLabel(*funDef); + } break; } default: diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cfb7de46e..47be4039a 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -107,8 +107,11 @@ ASTPointer Parser::parse(shared_ptr const& _scanner) case Token::Enum: nodes.push_back(parseEnumDefinition()); break; + case Token::Function: + nodes.push_back(parseFunctionDefinition(true)); + break; default: - fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum definition."); + fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition."); } } solAssert(m_recursionDepth == 0, ""); @@ -548,7 +551,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _isStateVari return result; } -ASTPointer Parser::parseFunctionDefinition() +ASTPointer Parser::parseFunctionDefinition(bool _freeFunction) { RecursionGuard recursionGuard(*this); ASTNodeFactory nodeFactory(*this); @@ -607,6 +610,7 @@ ASTPointer Parser::parseFunctionDefinition() name, header.visibility, header.stateMutability, + _freeFunction, kind, header.isVirtual, header.overrides, diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index b769545da..0047ed6e4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -92,7 +92,7 @@ private: ASTPointer parseOverrideSpecifier(); StateMutability parseStateMutability(); FunctionHeaderParserResult parseFunctionHeader(bool _isStateVariable); - ASTPointer parseFunctionDefinition(); + ASTPointer parseFunctionDefinition(bool _freeFunction = false); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); ASTPointer parseEnumValue(); diff --git a/test/libsolidity/semanticTests/freeFunctions/easy.sol b/test/libsolidity/semanticTests/freeFunctions/easy.sol new file mode 100644 index 000000000..04b023d52 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/easy.sol @@ -0,0 +1,13 @@ +function add(uint a, uint b) pure returns (uint) { + return a + b; +} + +contract C { + function f(uint x) public pure returns (uint) { + return add(x, 2); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256): 7 -> 9 diff --git a/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol new file mode 100644 index 000000000..7a05ff5d0 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/free_runtimecode.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (bool) { + return type(C).runtimeCode.length > 20; +} + +contract D { + function f() public returns (bool) { + return test(); + } +} +// ---- +// f() -> true diff --git a/test/libsolidity/semanticTests/freeFunctions/import.sol b/test/libsolidity/semanticTests/freeFunctions/import.sol new file mode 100644 index 000000000..edd44de49 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/import.sol @@ -0,0 +1,18 @@ +==== Source: A ==== +struct S { uint x; } +function set(S storage a, uint v) { a.x = v; } + +==== Source: B ==== +import "A"; +import "A" as A; +contract C { + A.S data; + function f(uint v) public returns (uint one, uint two) { + A.set(data, v); + one = data.x; + set(data, v + 1); + two = data.x; + } +} +// ---- +// f(uint256): 7 -> 7, 8 diff --git a/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol new file mode 100644 index 000000000..3632f6a6a --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/libraries_from_free.sol @@ -0,0 +1,21 @@ +library L { + function pub() public pure returns (uint) { + return 7; + } + function inter() internal pure returns (uint) { + return 8; + } +} + +function fu() pure returns (uint, uint) { + return (L.pub(), L.inter()); +} + +contract C { + function f() public pure returns (uint, uint) { + return fu(); + } +} +// ---- +// library: L +// f() -> 7, 8 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/new_operator.sol b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol new file mode 100644 index 000000000..e35f1c9ea --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/new_operator.sol @@ -0,0 +1,15 @@ +contract C { + uint public x = 2; +} + +function test() returns (uint) { + return (new C()).x(); +} + +contract D { + function f() public returns (uint) { + return test(); + } +} +// ---- +// f() -> 2 diff --git a/test/libsolidity/semanticTests/freeFunctions/overloads.sol b/test/libsolidity/semanticTests/freeFunctions/overloads.sol new file mode 100644 index 000000000..9b2a914eb --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/overloads.sol @@ -0,0 +1,16 @@ +function f(uint) returns (uint) { + return 2; +} +function f(string memory) returns (uint) { + return 3; +} + +contract C { + function g() public returns (uint, uint) { + return (f(2), f("abc")); + } +} +// ==== +// compileViaYul: also +// ---- +// g() -> 2, 3 diff --git a/test/libsolidity/semanticTests/freeFunctions/recursion.sol b/test/libsolidity/semanticTests/freeFunctions/recursion.sol new file mode 100644 index 000000000..fd21fe097 --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/recursion.sol @@ -0,0 +1,23 @@ +function exp(uint base, uint exponent) pure returns (uint power) { + if (exponent == 0) + return 1; + power = exp(base, exponent / 2); + power *= power; + if (exponent & 1 == 1) + power *= base; +} + +contract C { + function g(uint base, uint exponent) public pure returns (uint) { + return exp(base, exponent); + } +} +// ==== +// compileViaYul: also +// ---- +// g(uint256,uint256): 0, 0 -> 1 +// g(uint256,uint256): 0, 1 -> 0x00 +// g(uint256,uint256): 1, 0 -> 1 +// g(uint256,uint256): 2, 3 -> 8 +// g(uint256,uint256): 3, 10 -> 59049 +// g(uint256,uint256): 2, 255 -> -57896044618658097711785492504343953926634992332820282019728792003956564819968 \ No newline at end of file diff --git a/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol new file mode 100644 index 000000000..3b94b06fc --- /dev/null +++ b/test/libsolidity/semanticTests/freeFunctions/storage_calldata_refs.sol @@ -0,0 +1,17 @@ +contract C { + uint[] data; + function f(uint x, uint[] calldata input) public returns (uint, uint) { + data.push(x); + (uint a, uint[] calldata b) = fun(input, data); + return (a, b[1]); + + } +} + +function fun(uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256[]): 7, 0x40, 3, 8, 9, 10 -> 7, 9 diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol new file mode 100644 index 000000000..749a98d48 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_call_via_contract_type.sol @@ -0,0 +1,8 @@ +contract C { + function f() public pure {} +} +function fun() { + C.f(); +} +// ---- +// TypeError 3419: (68-73): Cannot call function via contract type name. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol new file mode 100644 index 000000000..afb375ca0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_constructor.sol @@ -0,0 +1,3 @@ +constructor() {} +// ---- +// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol new file mode 100644 index 000000000..31b410211 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_fallback.sol @@ -0,0 +1,3 @@ +fallback(){} +// ---- +// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol new file mode 100644 index 000000000..dd702853a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_modifier.sol @@ -0,0 +1,4 @@ +function fun() someModifier { +} +// ---- +// DeclarationError 7576: (15-27): Undeclared identifier. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol new file mode 100644 index 000000000..779d44c72 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_qualified_modifier.sol @@ -0,0 +1,9 @@ +contract C { + modifier someModifier() { _; } +} + +function fun() C.someModifier { + +} +// ---- +// ParserError 2314: (65-66): Expected '{' but got '.' diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol new file mode 100644 index 000000000..bd1b2bd87 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_shadowing.sol @@ -0,0 +1,9 @@ +function f() {} +contract C { + function f() public {} + function g() public { + f(); + } +} +// ---- +// Warning 2519: (31-53): This declaration shadows an existing declaration. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol new file mode 100644 index 000000000..36a0e0e0c --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_visibility.sol @@ -0,0 +1,5 @@ +function fun1() public { } +function fun2() internal { } +// ---- +// SyntaxError 4126: (0-26): Free functions cannot have visibility. +// SyntaxError 4126: (27-55): Free functions cannot have visibility. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol new file mode 100644 index 000000000..2fec9db1d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_function_without_body.sol @@ -0,0 +1,3 @@ +function f(); +// ---- +// TypeError 4668: (0-13): Free functions must be implemented. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol new file mode 100644 index 000000000..4862e8e63 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_functions.sol @@ -0,0 +1,4 @@ +function fun(uint256, uint[] calldata _x, uint[] storage _y) view returns (uint, uint[] calldata) { + return (_y[0], _x); +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol new file mode 100644 index 000000000..dafa91ccb --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_mutability.sol @@ -0,0 +1,8 @@ +function f() { + uint x = 2; + x; +} +function g(uint[] storage x) pure { x[0] = 1; } +// ---- +// Warning 2018: (0-39): Function state mutability can be restricted to pure +// TypeError 8961: (76-80): Function declared as pure, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol new file mode 100644 index 000000000..440b9449f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_overload.sol @@ -0,0 +1,9 @@ +function f(uint) returns (bytes memory) {} +function f(uint[] memory x) returns (bytes memory) { return f(x[0]); } +function g(uint8) {} +function g(uint16) {} +function t() { + g(2); +} +// ---- +// TypeError 4487: (176-177): No unique declaration found after argument-dependent lookup. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_override.sol b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol new file mode 100644 index 000000000..da54a2fa6 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_override.sol @@ -0,0 +1,4 @@ +function fun() override { +} +// ---- +// SyntaxError 1750: (0-27): Free functions cannot override. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol new file mode 100644 index 000000000..563c118f0 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_payable.sol @@ -0,0 +1,4 @@ +function fun() payable { +} +// ---- +// TypeError 9559: (0-26): Free functions cannot be payable. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol new file mode 100644 index 000000000..5801b9e9e --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_receive.sol @@ -0,0 +1,3 @@ +receive() {} +// ---- +// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition. diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol new file mode 100644 index 000000000..7d356a9a7 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_storage.sol @@ -0,0 +1,4 @@ +struct S { uint x; } +function fun(S storage) { +} +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol new file mode 100644 index 000000000..a4261be1f --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/free_virtual.sol @@ -0,0 +1,4 @@ +function fun() virtual { +} +// ---- +// SyntaxError 4493: (0-26): Free functions cannot be virtual. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol new file mode 100644 index 000000000..3b16749ae --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_same_name_as_contract.sol @@ -0,0 +1,4 @@ +contract C {} +function C() {} +// ---- +// DeclarationError 2333: (14-29): Identifier already declared. diff --git a/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol new file mode 100644 index 000000000..b029c0d90 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/function_using_struct_after_contract.sol @@ -0,0 +1,6 @@ +contract C { + struct S { uint x; } +} +function f() returns (uint) { S storage t; } +// ---- +// DeclarationError 7920: (70-71): Identifier not found or not unique. diff --git a/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol new file mode 100644 index 000000000..7e2dafe8a --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/gas_value.sol @@ -0,0 +1,6 @@ +function fun() { + fun{gas: 1}(); + fun{value: 1}(); +} +// ---- +// TypeError 2193: (21-32): Function call options can only be set on external function calls or contract creations. diff --git a/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol new file mode 100644 index 000000000..ab027e21d --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/qualified_struct_access.sol @@ -0,0 +1,7 @@ +function f() returns (uint) { C.S storage t; t.x; } + +contract C { + struct S { uint x; } +} +// ---- +// TypeError 3464: (45-46): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol new file mode 100644 index 000000000..c41693e97 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/struct_after_function.sol @@ -0,0 +1,3 @@ +function f(S storage g) view returns (uint) { S storage t = g; return t.x; } +struct S { uint x; } +// ---- diff --git a/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol new file mode 100644 index 000000000..ff6061c65 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/super_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + super; +} +// ---- +// DeclarationError 7576: (33-38): Undeclared identifier. "super" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol new file mode 100644 index 000000000..cdea708d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/freeFunctions/this_in_free_function.sol @@ -0,0 +1,6 @@ +contract C {} +function f() { + this; +} +// ---- +// DeclarationError 7576: (33-37): Undeclared identifier. "this" is not (or not yet) visible at this point. diff --git a/test/libsolidity/syntaxTests/imports/importing_free_functions.sol b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol new file mode 100644 index 000000000..02bc3673e --- /dev/null +++ b/test/libsolidity/syntaxTests/imports/importing_free_functions.sol @@ -0,0 +1,14 @@ +==== Source: a ==== +function f(uint x) pure returns (uint) { return x * 3; } +==== Source: b ==== +import "a" as A; +function g(uint x) pure returns (uint) { return A.f(x) * 3; } +==== Source: c ==== +import "b" as B; +contract C { + function f() public pure { + B.g(2); + B.A.f(3); + } +} +// ----