mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #8105 from ethereum/functionTypeRefactor
Add a new Function Type referring to FunctionDefinition's without calling context and use it to allow selector lookup.
This commit is contained in:
		
						commit
						a4e34b378a
					
				| @ -1,6 +1,7 @@ | ||||
| ### 0.6.2 (unreleased) | ||||
| 
 | ||||
| Language Features: | ||||
|  * Allow accessing external functions via contract and interface names to obtain their selector. | ||||
| 
 | ||||
| 
 | ||||
| Compiler Features: | ||||
|  | ||||
| @ -1691,6 +1691,16 @@ void TypeChecker::typeCheckFunctionCall( | ||||
| 	solAssert(!!_functionType, ""); | ||||
| 	solAssert(_functionType->kind() != FunctionType::Kind::ABIDecode, ""); | ||||
| 
 | ||||
| 	if (_functionType->kind() == FunctionType::Kind::Declaration) | ||||
| 	{ | ||||
| 		m_errorReporter.typeError( | ||||
| 			_functionCall.location(), | ||||
| 			"Cannot call function via contract name." | ||||
| 		); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 
 | ||||
| 	// Check for unsupported use of bare static call
 | ||||
| 	if ( | ||||
| 		_functionType->kind() == FunctionType::Kind::BareStaticCall && | ||||
|  | ||||
| @ -199,7 +199,7 @@ vector<pair<util::FixedHash<4>, FunctionTypePointer>> const& ContractDefinition: | ||||
| 			vector<FunctionTypePointer> functions; | ||||
| 			for (FunctionDefinition const* f: contract->definedFunctions()) | ||||
| 				if (f->isPartOfExternalInterface()) | ||||
| 					functions.push_back(TypeProvider::function(*f, false)); | ||||
| 					functions.push_back(TypeProvider::function(*f, FunctionType::Kind::External)); | ||||
| 			for (VariableDeclaration const* v: contract->stateVariables()) | ||||
| 				if (v->isPartOfExternalInterface()) | ||||
| 					functions.push_back(TypeProvider::function(*v)); | ||||
| @ -311,7 +311,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const | ||||
| 		case Visibility::Private: | ||||
| 		case Visibility::Internal: | ||||
| 		case Visibility::Public: | ||||
| 			return TypeProvider::function(*this, _internal); | ||||
| 			return TypeProvider::function(*this, FunctionType::Kind::Internal); | ||||
| 		case Visibility::External: | ||||
| 			return {}; | ||||
| 		} | ||||
| @ -327,7 +327,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const | ||||
| 			return {}; | ||||
| 		case Visibility::Public: | ||||
| 		case Visibility::External: | ||||
| 			return TypeProvider::function(*this, _internal); | ||||
| 			return TypeProvider::function(*this, FunctionType::Kind::External); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -338,7 +338,7 @@ FunctionTypePointer FunctionDefinition::functionType(bool _internal) const | ||||
| TypePointer FunctionDefinition::type() const | ||||
| { | ||||
| 	solAssert(visibility() != Visibility::External, ""); | ||||
| 	return TypeProvider::function(*this); | ||||
| 	return TypeProvider::function(*this, FunctionType::Kind::Internal); | ||||
| } | ||||
| 
 | ||||
| string FunctionDefinition::externalSignature() const | ||||
|  | ||||
| @ -414,9 +414,9 @@ ReferenceType const* TypeProvider::withLocation(ReferenceType const* _type, Data | ||||
| 	return static_cast<ReferenceType const*>(instance().m_generalTypes.back().get()); | ||||
| } | ||||
| 
 | ||||
| FunctionType const* TypeProvider::function(FunctionDefinition const& _function, bool _isInternal) | ||||
| FunctionType const* TypeProvider::function(FunctionDefinition const& _function, FunctionType::Kind _kind) | ||||
| { | ||||
| 	return createAndGet<FunctionType>(_function, _isInternal); | ||||
| 	return createAndGet<FunctionType>(_function, _kind); | ||||
| } | ||||
| 
 | ||||
| FunctionType const* TypeProvider::function(VariableDeclaration const& _varDecl) | ||||
|  | ||||
| @ -120,8 +120,8 @@ public: | ||||
| 		return _type; | ||||
| 	} | ||||
| 
 | ||||
| 	/// @returns the internally-facing or externally-facing type of a function.
 | ||||
| 	static FunctionType const* function(FunctionDefinition const& _function, bool _isInternal = true); | ||||
| 	/// @returns the internally-facing or externally-facing type of a function or the type of a function declaration.
 | ||||
| 	static FunctionType const* function(FunctionDefinition const& _function, FunctionType::Kind _kind = FunctionType::Kind::Declaration); | ||||
| 
 | ||||
| 	/// @returns the accessor function type of a state variable.
 | ||||
| 	static FunctionType const* function(VariableDeclaration const& _varDecl); | ||||
|  | ||||
| @ -362,7 +362,7 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition | ||||
| 				seenFunctions.insert(function); | ||||
| 				if (function->parameters().empty()) | ||||
| 					continue; | ||||
| 				FunctionTypePointer fun = FunctionType(*function, false).asCallableFunction(true, true); | ||||
| 				FunctionTypePointer fun = FunctionType(*function, FunctionType::Kind::External).asCallableFunction(true, true); | ||||
| 				if (_type.isImplicitlyConvertibleTo(*fun->selfType())) | ||||
| 					members.emplace_back(function->name(), fun, function); | ||||
| 			} | ||||
| @ -1933,7 +1933,7 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con | ||||
| 				if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) | ||||
| 					continue; | ||||
| 
 | ||||
| 				auto functionType = TypeProvider::function(*function, true); | ||||
| 				auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal); | ||||
| 				bool functionWithEqualArgumentsFound = false; | ||||
| 				for (auto const& member: members) | ||||
| 				{ | ||||
| @ -2465,12 +2465,16 @@ TypePointer TupleType::closestTemporaryType(Type const* _targetType) const | ||||
| 	return TypeProvider::tuple(move(tempComponents)); | ||||
| } | ||||
| 
 | ||||
| FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): | ||||
| 	m_kind(_isInternal ? Kind::Internal : Kind::External), | ||||
| FunctionType::FunctionType(FunctionDefinition const& _function, Kind _kind): | ||||
| 	m_kind(_kind), | ||||
| 	m_stateMutability(_function.stateMutability()), | ||||
| 	m_declaration(&_function) | ||||
| { | ||||
| 	if (_isInternal && m_stateMutability == StateMutability::Payable) | ||||
| 	solAssert( | ||||
| 		_kind == Kind::Internal || _kind == Kind::External || _kind == Kind::Declaration, | ||||
| 		"Only internal or external function types or function declaration types can be created from function definitions." | ||||
| 	); | ||||
| 	if (_kind == Kind::Internal && m_stateMutability == StateMutability::Payable) | ||||
| 		m_stateMutability = StateMutability::NonPayable; | ||||
| 
 | ||||
| 	for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) | ||||
| @ -2689,6 +2693,7 @@ string FunctionType::richIdentifier() const | ||||
| 	string id = "t_function_"; | ||||
| 	switch (m_kind) | ||||
| 	{ | ||||
| 	case Kind::Declaration: id += "declaration"; break; | ||||
| 	case Kind::Internal: id += "internal"; break; | ||||
| 	case Kind::External: id += "external"; break; | ||||
| 	case Kind::DelegateCall: id += "delegatecall"; break; | ||||
| @ -2755,7 +2760,12 @@ bool FunctionType::operator==(Type const& _other) const | ||||
| 
 | ||||
| BoolResult FunctionType::isExplicitlyConvertibleTo(Type const& _convertTo) const | ||||
| { | ||||
| 	return _convertTo.category() == category(); | ||||
| 	if (_convertTo.category() == category()) | ||||
| 	{ | ||||
| 		auto const& convertToType = dynamic_cast<FunctionType const&>(_convertTo); | ||||
| 		return (m_kind == FunctionType::Kind::Declaration) == (convertToType.kind() == FunctionType::Kind::Declaration); | ||||
| 	} | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| BoolResult FunctionType::isImplicitlyConvertibleTo(Type const& _convertTo) const | ||||
| @ -2808,7 +2818,18 @@ string FunctionType::canonicalName() const | ||||
| 
 | ||||
| string FunctionType::toString(bool _short) const | ||||
| { | ||||
| 	string name = "function ("; | ||||
| 	string name = "function "; | ||||
| 	if (m_kind == Kind::Declaration) | ||||
| 	{ | ||||
| 		auto const* functionDefinition = dynamic_cast<FunctionDefinition const*>(m_declaration); | ||||
| 		solAssert(functionDefinition, ""); | ||||
| 		auto const* contract = dynamic_cast<ContractDefinition const*>(functionDefinition->scope()); | ||||
| 		solAssert(contract, ""); | ||||
| 		name += contract->annotation().canonicalName; | ||||
| 		name += '.'; | ||||
| 		name += functionDefinition->name(); | ||||
| 	} | ||||
| 	name += '('; | ||||
| 	for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) | ||||
| 		name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); | ||||
| 	name += ")"; | ||||
| @ -2940,6 +2961,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con | ||||
| { | ||||
| 	switch (m_kind) | ||||
| 	{ | ||||
| 	case Kind::Declaration: | ||||
| 		return {{"selector", TypeProvider::fixedBytes(4)}}; | ||||
| 	case Kind::External: | ||||
| 	case Kind::Creation: | ||||
| 	case Kind::BareCall: | ||||
| @ -3146,6 +3169,7 @@ string FunctionType::externalSignature() const | ||||
| 	case Kind::External: | ||||
| 	case Kind::DelegateCall: | ||||
| 	case Kind::Event: | ||||
| 	case Kind::Declaration: | ||||
| 		break; | ||||
| 	default: | ||||
| 		solAssert(false, "Invalid function type for requesting external signature."); | ||||
| @ -3210,6 +3234,7 @@ TypePointers FunctionType::parseElementaryTypeVector(strings const& _types) | ||||
| 
 | ||||
| TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) const | ||||
| { | ||||
| 	solAssert(m_kind != Kind::Declaration, ""); | ||||
| 	return TypeProvider::function( | ||||
| 		m_parameterTypes, | ||||
| 		m_returnParameterTypes, | ||||
| @ -3387,14 +3412,6 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current | ||||
| 			auto const& currentBases = _currentScope->annotation().linearizedBaseContracts; | ||||
| 			isBase = (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()); | ||||
| 		} | ||||
| 		if (contract.isLibrary()) | ||||
| 			for (FunctionDefinition const* function: contract.definedFunctions()) | ||||
| 				if (function->isVisibleAsLibraryMember()) | ||||
| 					members.emplace_back( | ||||
| 						function->name(), | ||||
| 						FunctionType(*function).asCallableFunction(true), | ||||
| 						function | ||||
| 					); | ||||
| 		if (isBase) | ||||
| 		{ | ||||
| 			// We are accessing the type of a base contract, so add all public and protected
 | ||||
| @ -3404,6 +3421,17 @@ MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _current | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			bool inLibrary = contract.isLibrary(); | ||||
| 			for (FunctionDefinition const* function: contract.definedFunctions()) | ||||
| 				if ( | ||||
| 					(inLibrary && function->isVisibleAsLibraryMember()) || | ||||
| 					(!inLibrary && function->isPartOfExternalInterface()) | ||||
| 				) | ||||
| 					members.emplace_back( | ||||
| 						function->name(), | ||||
| 						FunctionType(*function).asCallableFunction(inLibrary), | ||||
| 						function | ||||
| 					); | ||||
| 			for (auto const& stru: contract.definedStructs()) | ||||
| 				members.emplace_back(stru->name(), stru->type(), stru); | ||||
| 			for (auto const& enu: contract.definedEnums()) | ||||
|  | ||||
| @ -1051,11 +1051,16 @@ public: | ||||
| 		ABIEncodeWithSignature, | ||||
| 		ABIDecode, | ||||
| 		GasLeft, ///< gasleft()
 | ||||
| 		MetaType ///< type(...)
 | ||||
| 		MetaType, ///< type(...)
 | ||||
| 		/// Refers to a function declaration without calling context
 | ||||
| 		/// (i.e. when accessed directly via the name of the containing contract).
 | ||||
| 		/// Cannot be called.
 | ||||
| 		Declaration | ||||
| 	}; | ||||
| 
 | ||||
| 	/// Creates the type of a function.
 | ||||
| 	explicit FunctionType(FunctionDefinition const& _function, bool _isInternal = true); | ||||
| 	/// @arg _kind must be Kind::Internal, Kind::External or Kind::Declaration.
 | ||||
| 	explicit FunctionType(FunctionDefinition const& _function, Kind _kind = Kind::Declaration); | ||||
| 	/// Creates the accessor function type of a state variable.
 | ||||
| 	explicit FunctionType(VariableDeclaration const& _varDecl); | ||||
| 	/// Creates the function type of an event.
 | ||||
| @ -1066,7 +1071,7 @@ public: | ||||
| 	FunctionType( | ||||
| 		strings const& _parameterTypes, | ||||
| 		strings const& _returnParameterTypes, | ||||
| 		Kind _kind = Kind::Internal, | ||||
| 		Kind _kind, | ||||
| 		bool _arbitraryParameters = false, | ||||
| 		StateMutability _stateMutability = StateMutability::NonPayable | ||||
| 	): FunctionType( | ||||
|  | ||||
| @ -550,6 +550,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) | ||||
| 			solAssert(function.kind() == FunctionType::Kind::DelegateCall || function.kind() == FunctionType::Kind::Internal, ""); | ||||
| 		switch (function.kind()) | ||||
| 		{ | ||||
| 		case FunctionType::Kind::Declaration: | ||||
| 			solAssert(false, "Attempted to generate code for calling a function definition."); | ||||
| 			break; | ||||
| 		case FunctionType::Kind::Internal: | ||||
| 		{ | ||||
| 			// Calling convention: Caller pushes return address and arguments
 | ||||
| @ -1289,14 +1292,22 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) | ||||
| 			_memberAccess.expression().accept(*this); | ||||
| 		return false; | ||||
| 	} | ||||
| 	// Another special case for `this.f.selector` which does not need the address.
 | ||||
| 	// There are other uses of `.selector` which do need the address, but we want this
 | ||||
| 	// specific use to be a pure expression.
 | ||||
| 	// Another special case for `this.f.selector` and for ``C.f.selector`` which do not need the address.
 | ||||
| 	// There are other uses of `.selector` which do need the address, but we want these
 | ||||
| 	// specific uses to be pure expressions.
 | ||||
| 	if ( | ||||
| 		_memberAccess.expression().annotation().type->category() == Type::Category::Function && | ||||
| 		member == "selector" | ||||
| 		auto const* functionType = dynamic_cast<FunctionType const*>(_memberAccess.expression().annotation().type); | ||||
| 		functionType && member == "selector" | ||||
| 	) | ||||
| 		if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression())) | ||||
| 	{ | ||||
| 		if (functionType->kind() == FunctionType::Kind::Declaration) | ||||
| 		{ | ||||
| 			m_context << functionType->externalIdentifier(); | ||||
| 			/// need to store it as bytes4
 | ||||
| 			utils().leftShiftNumberOnStack(224); | ||||
| 			return false; | ||||
| 		} | ||||
| 		else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression())) | ||||
| 			if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression())) | ||||
| 				if (exprInt->name() == "this") | ||||
| 					if (Declaration const* declaration = expr->annotation().referencedDeclaration) | ||||
| @ -1313,6 +1324,7 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) | ||||
| 						utils().leftShiftNumberOnStack(224); | ||||
| 						return false; | ||||
| 					} | ||||
| 	} | ||||
| 	// Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance
 | ||||
| 	// opcode.
 | ||||
| 	if ( | ||||
|  | ||||
| @ -75,7 +75,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) | ||||
| 	} | ||||
| 	if (_contractDef.constructor()) | ||||
| 	{ | ||||
| 		FunctionType constrType(*_contractDef.constructor(), false); | ||||
| 		FunctionType constrType(*_contractDef.constructor()); | ||||
| 		FunctionType const* externalFunctionType = constrType.interfaceFunctionType(); | ||||
| 		solAssert(!!externalFunctionType, ""); | ||||
| 		Json::Value method; | ||||
| @ -92,7 +92,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef) | ||||
| 	for (auto const* fallbackOrReceive: {_contractDef.fallbackFunction(), _contractDef.receiveFunction()}) | ||||
| 		if (fallbackOrReceive) | ||||
| 		{ | ||||
| 			FunctionType const* externalFunctionType = FunctionType(*fallbackOrReceive, false).interfaceFunctionType(); | ||||
| 			auto const* externalFunctionType = FunctionType(*fallbackOrReceive).interfaceFunctionType(); | ||||
| 			solAssert(!!externalFunctionType, ""); | ||||
| 			Json::Value method; | ||||
| 			method["type"] = TokenTraits::toString(fallbackOrReceive->kind()); | ||||
|  | ||||
| @ -0,0 +1,20 @@ | ||||
| contract A { | ||||
|     function f() external {} | ||||
|     function g(uint256) external {} | ||||
| } | ||||
| contract B { | ||||
|     function f() external returns (uint256) {} | ||||
|     function g(uint256) external returns (uint256) {} | ||||
| } | ||||
| contract C { | ||||
|     function test1() external returns(bytes4, bytes4, bytes4, bytes4) { | ||||
|         return (A.f.selector, A.g.selector, B.f.selector, B.g.selector); | ||||
|     } | ||||
|     function test2() external returns(bytes4, bytes4, bytes4, bytes4) { | ||||
|         A a; B b; | ||||
|         return (a.f.selector, a.g.selector, b.f.selector, b.g.selector); | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // test1() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a) | ||||
| // test2() -> left(0x26121ff0), left(0xe420264a), left(0x26121ff0), left(0xe420264a) | ||||
| @ -0,0 +1,14 @@ | ||||
| contract A { | ||||
|     function f() external {} | ||||
|     function g() external pure {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function h() external { | ||||
|         function() external f = A.f; | ||||
|         function() external pure g = A.g; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (128-155): Type function A.f() is not implicitly convertible to expected type function () external. | ||||
| // TypeError: (165-197): Type function A.g() pure is not implicitly convertible to expected type function () pure external. | ||||
| @ -0,0 +1,17 @@ | ||||
| contract A { | ||||
|     function f() external {} | ||||
|     function g() external pure {} | ||||
|     function h() public pure {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function i() external { | ||||
|         A.f(); | ||||
|         A.g(); | ||||
|         A.h(); // might be allowed in the future | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (160-165): Cannot call function via contract name. | ||||
| // TypeError: (175-180): Cannot call function via contract name. | ||||
| // TypeError: (190-195): Cannot call function via contract name. | ||||
| @ -0,0 +1,9 @@ | ||||
| contract A { | ||||
|     function f() external {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external pure { | ||||
|         A.f.selector; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,9 @@ | ||||
| interface I { | ||||
|     function f() external; | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external pure { | ||||
|         I.f.selector; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| contract A { | ||||
|     function f() internal {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external { | ||||
|         A.f; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (94-97): Member "f" not found or not visible after argument-dependent lookup in type(contract A). | ||||
| @ -0,0 +1,12 @@ | ||||
| contract A { | ||||
|     function f() external {} | ||||
|     function f(uint256) external {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external { | ||||
|         A.f; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (130-133): Member "f" not unique after argument-dependent lookup in type(contract A). | ||||
| @ -0,0 +1,11 @@ | ||||
| contract A { | ||||
|     function f() private {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external { | ||||
|         A.f; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError: (93-96): Member "f" not found or not visible after argument-dependent lookup in type(contract A). | ||||
| @ -0,0 +1,9 @@ | ||||
| contract A { | ||||
|     function f() public {} | ||||
| } | ||||
| 
 | ||||
| contract B { | ||||
|     function g() external pure { | ||||
|         A.f.selector; | ||||
|     } | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user