mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Relax restrictions on immutable initialization
This commit is contained in:
		
							parent
							
								
									490b90d0ab
								
							
						
					
					
						commit
						dad2bf6472
					
				| @ -2,6 +2,7 @@ | ||||
| 
 | ||||
| Language Features: | ||||
|  * Allow qualified access to events from other contracts. | ||||
|  * Relax restrictions on initialization of immutable variables. Reads and writes may now happen at any point at construction time outside of functions and modifiers. Explicit initialization is no longer mandatory. | ||||
| 
 | ||||
| Compiler Features: | ||||
|  * Commandline Interface: Add ``--ast-compact-json`` output in assembler mode. | ||||
|  | ||||
| @ -150,7 +150,7 @@ Modifiers | ||||
| - ``view`` for functions: Disallows modification of state. | ||||
| - ``payable`` for functions: Allows them to receive Ether together with a call. | ||||
| - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. | ||||
| - ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code. | ||||
| - ``immutable`` for state variables: Allows assignment at construction time and is constant when deployed. Is stored in code. | ||||
| - ``anonymous`` for events: Does not store event signature as topic. | ||||
| - ``indexed`` for event parameters: Stores the parameter as topic. | ||||
| - ``virtual`` for functions and modifiers: Allows the function's or modifier's | ||||
|  | ||||
| @ -30,19 +30,23 @@ Not all types for constants and immutables are implemented at this time. The onl | ||||
| .. code-block:: solidity | ||||
| 
 | ||||
|     // SPDX-License-Identifier: GPL-3.0 | ||||
|     pragma solidity >=0.7.4; | ||||
|     pragma solidity ^0.8.21; | ||||
| 
 | ||||
|     uint constant X = 32**22 + 8; | ||||
| 
 | ||||
|     contract C { | ||||
|         string constant TEXT = "abc"; | ||||
|         bytes32 constant MY_HASH = keccak256("abc"); | ||||
|         uint immutable decimals; | ||||
|         uint immutable decimals = 18; | ||||
|         uint immutable maxBalance; | ||||
|         address immutable owner = msg.sender; | ||||
| 
 | ||||
|         constructor(uint decimals_, address ref) { | ||||
|             decimals = decimals_; | ||||
|             if (decimals_ != 0) | ||||
|                 // Immutables are only immutable when deployed. | ||||
|                 // At construction time they can be assigned to any number of times. | ||||
|                 decimals = decimals_; | ||||
| 
 | ||||
|             // Assignments to immutables can even access the environment. | ||||
|             maxBalance = ref.balance; | ||||
|         } | ||||
| @ -74,10 +78,29 @@ Immutable | ||||
| ========= | ||||
| 
 | ||||
| Variables declared as ``immutable`` are a bit less restricted than those | ||||
| declared as ``constant``: Immutable variables can be assigned an arbitrary | ||||
| value in the constructor of the contract or at the point of their declaration. | ||||
| They can be assigned only once and can, from that point on, be read even during | ||||
| construction time. | ||||
| declared as ``constant``: Immutable variables can be assigned a | ||||
| value at construction time. | ||||
| The value can be changed at any time before deployment and then it becomes permanent. | ||||
| 
 | ||||
| One additional restriction is that immutables can only be assigned to inside expressions for which | ||||
| there is no possibility of being executed after creation. | ||||
| This excludes all modifier definitions and functions other than constructors. | ||||
| 
 | ||||
| There are no restrictions on reading immutable variables. | ||||
| The read is even allowed to happen before the variable is written to for the first time because variables in | ||||
| Solidity always have a well-defined initial value. | ||||
| For this reason it is also allowed to never explicitly assign a value to an immutable. | ||||
| 
 | ||||
| .. warning:: | ||||
|     When accessing immutables at construction time, please keep the :ref:`initialization order | ||||
|     <state-variable-initialization-order>` in mind. | ||||
|     Even if you provide an explicit initializer, some expressions may end up being evaluated before | ||||
|     that initializer, especially when they are at a different level in inheritance hierarchy. | ||||
| 
 | ||||
| .. note:: | ||||
|     Before Solidity 0.8.21 initialization of immutable variables was more restrictive. | ||||
|     Such variables had to be initialized exactly once at construction time and could not be read | ||||
|     before then. | ||||
| 
 | ||||
| The contract creation code generated by the compiler will modify the | ||||
| contract's runtime code before it is returned by replacing all references | ||||
| @ -86,14 +109,3 @@ you are comparing the | ||||
| runtime code generated by the compiler with the one actually stored in the | ||||
| blockchain. The compiler outputs where these immutables are located in the deployed bytecode | ||||
| in the ``immutableReferences`` field of the :ref:`compiler JSON standard output <compiler-api>`. | ||||
| 
 | ||||
| .. note:: | ||||
|   Immutables that are assigned at their declaration are only considered | ||||
|   initialized once the constructor of the contract is executing. | ||||
|   This means you cannot initialize immutables inline with a value | ||||
|   that depends on another immutable. You can do this, however, | ||||
|   inside the constructor of the contract. | ||||
| 
 | ||||
|   This is a safeguard against different interpretations about the order | ||||
|   of state variable initialization and constructor execution, especially | ||||
|   with regards to inheritance. | ||||
|  | ||||
| @ -30,6 +30,8 @@ Semantic Only Changes | ||||
| This section lists the changes that are semantic-only, thus potentially | ||||
| hiding new and different behavior in existing code. | ||||
| 
 | ||||
| .. _state-variable-initialization-order: | ||||
| 
 | ||||
| - The order of state variable initialization has changed in case of inheritance. | ||||
| 
 | ||||
|   The order used to be: | ||||
|  | ||||
| @ -18,8 +18,6 @@ | ||||
| 
 | ||||
| #include <libsolidity/analysis/ImmutableValidator.h> | ||||
| 
 | ||||
| #include <libsolutil/CommonData.h> | ||||
| 
 | ||||
| #include <range/v3/view/reverse.hpp> | ||||
| 
 | ||||
| using namespace solidity::frontend; | ||||
| @ -27,275 +25,44 @@ using namespace solidity::langutil; | ||||
| 
 | ||||
| void ImmutableValidator::analyze() | ||||
| { | ||||
| 	m_inCreationContext = true; | ||||
| 
 | ||||
| 	auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse; | ||||
| 
 | ||||
| 	for (ContractDefinition const* contract: linearizedContracts) | ||||
| 		for (VariableDeclaration const* stateVar: contract->stateVariables()) | ||||
| 			if (stateVar->value()) | ||||
| 				stateVar->value()->accept(*this); | ||||
| 
 | ||||
| 	for (ContractDefinition const* contract: linearizedContracts) | ||||
| 		for (std::shared_ptr<InheritanceSpecifier> const& inheritSpec: contract->baseContracts()) | ||||
| 			if (auto args = inheritSpec->arguments()) | ||||
| 				ASTNode::listAccept(*args, *this); | ||||
| 
 | ||||
| 	for (ContractDefinition const* contract: linearizedContracts) | ||||
| 	{ | ||||
| 		for (VariableDeclaration const* stateVar: contract->stateVariables()) | ||||
| 			if (stateVar->value()) | ||||
| 				m_initializedStateVariables.emplace(stateVar); | ||||
| 		for (FunctionDefinition const* function: contract->definedFunctions()) | ||||
| 			function->accept(*this); | ||||
| 
 | ||||
| 		if (contract->constructor()) | ||||
| 			visitCallableIfNew(*contract->constructor()); | ||||
| 		for (ModifierDefinition const* modifier: contract->functionModifiers()) | ||||
| 			modifier->accept(*this); | ||||
| 	} | ||||
| 
 | ||||
| 	m_inCreationContext = false; | ||||
| 
 | ||||
| 	for (ContractDefinition const* contract: linearizedContracts) | ||||
| 	{ | ||||
| 		for (auto funcDef: contract->definedFunctions()) | ||||
| 			visitCallableIfNew(*funcDef); | ||||
| 
 | ||||
| 		for (auto modDef: contract->functionModifiers()) | ||||
| 			visitCallableIfNew(*modDef); | ||||
| 	} | ||||
| 
 | ||||
| 	checkAllVariablesInitialized(m_mostDerivedContract.location()); | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(Assignment const& _assignment) | ||||
| { | ||||
| 	// Need to visit values first (rhs) as they might access other immutables.
 | ||||
| 	_assignment.rightHandSide().accept(*this); | ||||
| 	_assignment.leftHandSide().accept(*this); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition) | ||||
| { | ||||
| 	return analyseCallable(_functionDefinition); | ||||
| 	return !_functionDefinition.isConstructor(); | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition) | ||||
| void ImmutableValidator::endVisit(MemberAccess const& _memberAccess) | ||||
| { | ||||
| 	return analyseCallable(_modifierDefinition); | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(MemberAccess const& _memberAccess) | ||||
| { | ||||
| 	_memberAccess.expression().accept(*this); | ||||
| 
 | ||||
| 	if (auto contractType = dynamic_cast<ContractType const*>(_memberAccess.expression().annotation().type)) | ||||
| 		if (!contractType->isSuper()) | ||||
| 			// external access, no analysis needed.
 | ||||
| 			return false; | ||||
| 
 | ||||
| 	if (auto varDecl = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) | ||||
| 		analyseVariableReference(*varDecl, _memberAccess); | ||||
| 	else if (auto funcType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type)) | ||||
| 		if (funcType->kind() == FunctionType::Kind::Internal && funcType->hasDeclaration()) | ||||
| 			visitCallableIfNew(funcType->declaration()); | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(IfStatement const& _ifStatement) | ||||
| { | ||||
| 	bool prevInBranch = m_inBranch; | ||||
| 
 | ||||
| 	_ifStatement.condition().accept(*this); | ||||
| 
 | ||||
| 	m_inBranch = true; | ||||
| 	_ifStatement.trueStatement().accept(*this); | ||||
| 
 | ||||
| 	if (auto falseStatement = _ifStatement.falseStatement()) | ||||
| 		falseStatement->accept(*this); | ||||
| 
 | ||||
| 	m_inBranch = prevInBranch; | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(WhileStatement const& _whileStatement) | ||||
| { | ||||
| 	bool prevInLoop = m_inLoop; | ||||
| 	m_inLoop = true; | ||||
| 
 | ||||
| 	_whileStatement.condition().accept(*this); | ||||
| 	_whileStatement.body().accept(*this); | ||||
| 
 | ||||
| 	m_inLoop = prevInLoop; | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::visit(TryStatement const& _tryStatement) | ||||
| { | ||||
| 	ScopedSaveAndRestore constructorGuard{m_inTryStatement, true}; | ||||
| 
 | ||||
| 	_tryStatement.externalCall().accept(*this); | ||||
| 
 | ||||
| 	for (auto&& clause: _tryStatement.clauses()) | ||||
| 		if (clause) | ||||
| 			clause->accept(*this); | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::endVisit(IdentifierPath const& _identifierPath) | ||||
| { | ||||
| 	if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifierPath.annotation().referencedDeclaration)) | ||||
| 		visitCallableIfNew( | ||||
| 			*_identifierPath.annotation().requiredLookup == VirtualLookup::Virtual ? | ||||
| 			callableDef->resolveVirtual(m_mostDerivedContract) : | ||||
| 			*callableDef | ||||
| 		); | ||||
| 
 | ||||
| 	solAssert(!dynamic_cast<VariableDeclaration const*>(_identifierPath.annotation().referencedDeclaration), ""); | ||||
| 	analyseVariableReference(_memberAccess.annotation().referencedDeclaration, _memberAccess); | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::endVisit(Identifier const& _identifier) | ||||
| { | ||||
| 	if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration)) | ||||
| 		visitCallableIfNew(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual ? callableDef->resolveVirtual(m_mostDerivedContract) : *callableDef); | ||||
| 	if (auto const varDecl = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) | ||||
| 		analyseVariableReference(*varDecl, _identifier); | ||||
| 	analyseVariableReference(_identifier.annotation().referencedDeclaration, _identifier); | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::endVisit(Return const& _return) | ||||
| void ImmutableValidator::analyseVariableReference(Declaration const* _reference, Expression const& _expression) | ||||
| { | ||||
| 	if (m_currentConstructor != nullptr) | ||||
| 		checkAllVariablesInitialized(_return.location()); | ||||
| } | ||||
| 
 | ||||
| bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration) | ||||
| { | ||||
| 	ScopedSaveAndRestore constructorGuard{m_currentConstructor, {}}; | ||||
| 	ScopedSaveAndRestore constructorContractGuard{m_currentConstructorContract, {}}; | ||||
| 
 | ||||
| 	if (FunctionDefinition const* funcDef = dynamic_cast<decltype(funcDef)>(&_callableDeclaration)) | ||||
| 	{ | ||||
| 		ASTNode::listAccept(funcDef->modifiers(), *this); | ||||
| 
 | ||||
| 		if (funcDef->isConstructor()) | ||||
| 		{ | ||||
| 			m_currentConstructorContract = funcDef->annotation().contract; | ||||
| 			m_currentConstructor = funcDef; | ||||
| 		} | ||||
| 
 | ||||
| 		if (funcDef->isImplemented()) | ||||
| 			funcDef->body().accept(*this); | ||||
| 	} | ||||
| 	else if (ModifierDefinition const* modDef = dynamic_cast<decltype(modDef)>(&_callableDeclaration)) | ||||
| 		if (modDef->isImplemented()) | ||||
| 			modDef->body().accept(*this); | ||||
| 
 | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression) | ||||
| { | ||||
| 	if (!_variableReference.isStateVariable() || !_variableReference.immutable()) | ||||
| 	auto const* variable = dynamic_cast<VariableDeclaration const*>(_reference); | ||||
| 	if (!variable || !variable->isStateVariable() || !variable->immutable()) | ||||
| 		return; | ||||
| 
 | ||||
| 	// If this is not an ordinary assignment, we write and read at the same time.
 | ||||
| 	bool write = _expression.annotation().willBeWrittenTo; | ||||
| 	bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; | ||||
| 	if (write) | ||||
| 	{ | ||||
| 		if (!m_currentConstructor) | ||||
| 			m_errorReporter.typeError( | ||||
| 				1581_error, | ||||
| 				_expression.location(), | ||||
| 				"Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." | ||||
| 			); | ||||
| 		else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) | ||||
| 			m_errorReporter.typeError( | ||||
| 				7484_error, | ||||
| 				_expression.location(), | ||||
| 				"Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in." | ||||
| 			); | ||||
| 		else if (m_inLoop) | ||||
| 			m_errorReporter.typeError( | ||||
| 				6672_error, | ||||
| 				_expression.location(), | ||||
| 				"Cannot write to immutable here: Immutable variables cannot be initialized inside a loop." | ||||
| 			); | ||||
| 		else if (m_inBranch) | ||||
| 			m_errorReporter.typeError( | ||||
| 				4599_error, | ||||
| 				_expression.location(), | ||||
| 				"Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement." | ||||
| 			); | ||||
| 		else if (m_inTryStatement) | ||||
| 			m_errorReporter.typeError( | ||||
| 					4130_error, | ||||
| 					_expression.location(), | ||||
| 					"Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement." | ||||
| 			); | ||||
| 		else if (m_initializedStateVariables.count(&_variableReference)) | ||||
| 		{ | ||||
| 			if (!read) | ||||
| 				m_errorReporter.typeError( | ||||
| 					1574_error, | ||||
| 					_expression.location(), | ||||
| 					"Immutable state variable already initialized." | ||||
| 				); | ||||
| 			else | ||||
| 				m_errorReporter.typeError( | ||||
| 					2718_error, | ||||
| 					_expression.location(), | ||||
| 					"Immutable variables cannot be modified after initialization." | ||||
| 				); | ||||
| 		} | ||||
| 		else if (read) | ||||
| 			m_errorReporter.typeError( | ||||
| 				3969_error, | ||||
| 				_expression.location(), | ||||
| 				"Immutable variables must be initialized using an assignment." | ||||
| 			); | ||||
| 		m_initializedStateVariables.emplace(&_variableReference); | ||||
| 	} | ||||
| 	if ( | ||||
| 		read && | ||||
| 		m_inCreationContext && | ||||
| 		!m_initializedStateVariables.count(&_variableReference) | ||||
| 	) | ||||
| 	if (_expression.annotation().willBeWrittenTo) | ||||
| 		m_errorReporter.typeError( | ||||
| 			7733_error, | ||||
| 			1581_error, | ||||
| 			_expression.location(), | ||||
| 			"Immutable variables cannot be read before they are initialized." | ||||
| 			"Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." | ||||
| 		); | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location) | ||||
| { | ||||
| 	for (ContractDefinition const* contract: m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse) | ||||
| 	{ | ||||
| 		for (VariableDeclaration const* varDecl: contract->stateVariables()) | ||||
| 			if (varDecl->immutable()) | ||||
| 				if (!util::contains(m_initializedStateVariables, varDecl)) | ||||
| 					m_errorReporter.typeError( | ||||
| 						2658_error, | ||||
| 						_location, | ||||
| 						solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), | ||||
| 						"Construction control flow ends without initializing all immutable state variables." | ||||
| 					); | ||||
| 
 | ||||
| 		// Don't check further than the current c'tors contract
 | ||||
| 		if (contract == m_currentConstructorContract) | ||||
| 			break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration) | ||||
| { | ||||
| 	CallableDeclaration const* _callable = dynamic_cast<CallableDeclaration const*>(&_declaration); | ||||
| 	solAssert(_callable != nullptr, ""); | ||||
| 
 | ||||
| 	if (m_visitedCallables.emplace(_callable).second) | ||||
| 		_declaration.accept(*this); | ||||
| } | ||||
|  | ||||
| @ -21,25 +21,15 @@ | ||||
| #include <libsolidity/ast/ASTVisitor.h> | ||||
| #include <liblangutil/ErrorReporter.h> | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| namespace solidity::frontend | ||||
| { | ||||
| 
 | ||||
| /**
 | ||||
|  * Validates access and initialization of immutable variables: | ||||
|  * must be directly initialized in their respective c'tor or inline | ||||
|  * cannot be read before being initialized | ||||
|  * cannot be read when initializing state variables inline | ||||
|  * must be initialized outside loops (only one initialization) | ||||
|  * must be initialized outside ifs (must be initialized unconditionally) | ||||
|  * must be initialized exactly once (no multiple statements) | ||||
|  * must be initialized exactly once (no early return to skip initialization) | ||||
|  * must be directly initialized in a c'tor or inline | ||||
| */ | ||||
| class ImmutableValidator: private ASTConstVisitor | ||||
| { | ||||
| 	using CallableDeclarationSet = std::set<CallableDeclaration const*, ASTNode::CompareByID>; | ||||
| 
 | ||||
| public: | ||||
| 	ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): | ||||
| 		m_mostDerivedContract(_contractDefinition), | ||||
| @ -49,37 +39,15 @@ public: | ||||
| 	void analyze(); | ||||
| 
 | ||||
| private: | ||||
| 	bool visit(Assignment const& _assignment); | ||||
| 	bool visit(FunctionDefinition const& _functionDefinition); | ||||
| 	bool visit(ModifierDefinition const& _modifierDefinition); | ||||
| 	bool visit(MemberAccess const& _memberAccess); | ||||
| 	bool visit(IfStatement const& _ifStatement); | ||||
| 	bool visit(WhileStatement const& _whileStatement); | ||||
| 	bool visit(TryStatement const& _tryStatement); | ||||
| 	void endVisit(IdentifierPath const& _identifierPath); | ||||
| 	void endVisit(MemberAccess const& _memberAccess); | ||||
| 	void endVisit(Identifier const& _identifier); | ||||
| 	void endVisit(Return const& _return); | ||||
| 
 | ||||
| 	bool analyseCallable(CallableDeclaration const& _callableDeclaration); | ||||
| 	void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression); | ||||
| 
 | ||||
| 	void checkAllVariablesInitialized(langutil::SourceLocation const& _location); | ||||
| 
 | ||||
| 	void visitCallableIfNew(Declaration const& _declaration); | ||||
| 	void analyseVariableReference(Declaration const* _variableReference, Expression const& _expression); | ||||
| 
 | ||||
| 	ContractDefinition const& m_mostDerivedContract; | ||||
| 
 | ||||
| 	CallableDeclarationSet m_visitedCallables; | ||||
| 
 | ||||
| 	std::set<VariableDeclaration const*, ASTNode::CompareByID> m_initializedStateVariables; | ||||
| 	langutil::ErrorReporter& m_errorReporter; | ||||
| 
 | ||||
| 	FunctionDefinition const* m_currentConstructor = nullptr; | ||||
| 	ContractDefinition const* m_currentConstructorContract = nullptr; | ||||
| 	bool m_inLoop = false; | ||||
| 	bool m_inBranch = false; | ||||
| 	bool m_inCreationContext = true; | ||||
| 	bool m_inTryStatement = false; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
							
								
								
									
										15
									
								
								test/libsolidity/semanticTests/immutable/delete.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								test/libsolidity/semanticTests/immutable/delete.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| contract C { | ||||
|     uint8 immutable public a; | ||||
|     uint8 immutable public b = 0x42; | ||||
|     uint public c; | ||||
| 
 | ||||
|     constructor() { | ||||
|         delete a; | ||||
|         delete b; | ||||
|         c = b * 2 + a; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // a() -> 0 | ||||
| // b() -> 0 | ||||
| // c() -> 0 | ||||
| @ -0,0 +1,18 @@ | ||||
| contract C { | ||||
|     int immutable x = 1; | ||||
|     int immutable y = 3; | ||||
| 
 | ||||
|     constructor() { | ||||
|         x--; | ||||
|         --x; | ||||
|         y++; | ||||
|         ++y; | ||||
|         --y; | ||||
|     } | ||||
| 
 | ||||
|     function f() public view returns (int, int) { | ||||
|         return (x, y); | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // f() -> -1, 4 | ||||
| @ -0,0 +1,27 @@ | ||||
| contract A { | ||||
|     uint immutable x = x + 1; | ||||
|     uint immutable y = x += 2; | ||||
| 
 | ||||
|     constructor(uint) m(x += 16) m(x += 32) { | ||||
|         x += 64; | ||||
|         x += 128; | ||||
|     } | ||||
| 
 | ||||
|     modifier m(uint) { | ||||
|         _; | ||||
|     } | ||||
| 
 | ||||
|     function get() public returns (uint) { | ||||
|         return x; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| contract B is A(A.x += 8) { | ||||
|     constructor(uint) {} | ||||
| } | ||||
| 
 | ||||
| contract C is B { | ||||
|     constructor() B(x += 4) {} | ||||
| } | ||||
| // ---- | ||||
| // get() -> 0xff | ||||
							
								
								
									
										11
									
								
								test/libsolidity/semanticTests/immutable/uninitialized.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/libsolidity/semanticTests/immutable/uninitialized.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| contract C { | ||||
|     uint immutable u; | ||||
|     bool immutable b; | ||||
|     address immutable a; | ||||
| 
 | ||||
|     function get() public returns (uint, bool, address) { | ||||
|         return (u, b, a); | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // get() -> 0, false, 0x0 | ||||
| @ -7,5 +7,3 @@ contract C { | ||||
|         x = 1; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 2658: (86-93): Construction control flow ends without initializing all immutable state variables. | ||||
|  | ||||
| @ -5,5 +5,3 @@ contract C { | ||||
|             x = 1; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 4599: (86-87): Cannot write to immutable here: Immutable variables cannot be initialized inside an if statement. | ||||
|  | ||||
| @ -6,5 +6,3 @@ contract C { | ||||
| 
 | ||||
|     function f() public view returns (uint) { return 3 + x; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (136-137): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -4,5 +4,3 @@ contract C { | ||||
|         x = 3 + x; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (71-72): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -9,4 +9,3 @@ contract C { | ||||
|     function f(uint a) internal pure {} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (59-60): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -10,5 +10,3 @@ contract C { | ||||
| 
 | ||||
|     function f(uint a) internal pure {} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (119-120): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -4,5 +4,3 @@ contract C { | ||||
|         x--; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 3969: (63-64): Immutable variables must be initialized using an assignment. | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| contract C { | ||||
|     uint immutable x; | ||||
|     constructor() { | ||||
|         delete x; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 3969: (70-71): Immutable variables must be initialized using an assignment. | ||||
| @ -1,8 +0,0 @@ | ||||
| contract C { | ||||
|     uint immutable x = 3; | ||||
|     constructor() { | ||||
|         delete x; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 2718: (74-75): Immutable variables cannot be modified after initialization. | ||||
| @ -3,5 +3,3 @@ contract C { | ||||
| 
 | ||||
|     function f() public view returns (uint) { return 3 + x; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (99-100): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -11,4 +11,3 @@ contract C is B(C.f) { | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (200-201): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
| // TypeError 1574: (109-110): Immutable state variable already initialized. | ||||
|  | ||||
| @ -10,4 +10,3 @@ contract C is B(C.f) { | ||||
|     function f() internal view returns(uint) { return x + 2; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (205-206): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -4,5 +4,3 @@ contract C { | ||||
|         x++; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 3969: (63-64): Immutable variables must be initialized using an assignment. | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| contract C { | ||||
|   uint immutable x; | ||||
|   uint immutable y; | ||||
|   constructor() { | ||||
|     ++x; | ||||
|     --y; | ||||
|   } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 3969: (77-78): Immutable variables must be initialized using an assignment. | ||||
| // TypeError 3969: (86-87): Immutable variables must be initialized using an assignment. | ||||
| @ -5,4 +5,3 @@ contract C { | ||||
|     function f() internal pure returns(uint) { return x; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (112-113): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -10,5 +10,3 @@ contract C is B { | ||||
|     uint immutable y; | ||||
|     constructor() B(y = 3) { } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (148-149): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -9,5 +9,3 @@ contract B { | ||||
| contract C is B(C.y = 3) { | ||||
|     uint immutable y; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (104-107): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -12,5 +12,3 @@ contract C is B(C.y) { | ||||
|         y = 3; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (104-107): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -16,4 +16,3 @@ contract C is B { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (263-264): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -17,5 +17,3 @@ contract C is B { | ||||
|         _; f(x); | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (245-246): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -7,5 +7,3 @@ contract C is B { | ||||
|         x = 3; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7484: (88-89): Cannot write to immutable here: Immutable variables must be initialized in the constructor of the contract they are defined in. | ||||
|  | ||||
| @ -4,4 +4,3 @@ contract D { | ||||
|     constructor() m(t = 2) {} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (83-84): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -3,5 +3,3 @@ contract D is C { | ||||
|   uint immutable t; | ||||
|   constructor() C(t=2) {} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (92-93): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -0,0 +1,7 @@ | ||||
| contract D { | ||||
|     uint immutable t; | ||||
|     modifier m(uint) { _; } | ||||
|     function f() public m(t = 2) {} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (89-90): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
| @ -0,0 +1,11 @@ | ||||
| contract B { | ||||
|     uint immutable x; | ||||
| } | ||||
| 
 | ||||
| contract C is B { | ||||
|     function f() public { | ||||
|         B.x = 42; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (90-93): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
| @ -0,0 +1,13 @@ | ||||
| contract B { | ||||
|     uint immutable x; | ||||
| 
 | ||||
|     function g() public {} | ||||
| } | ||||
| 
 | ||||
| contract C is B { | ||||
|     function f() public { | ||||
|         super.x = 42; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 9582: (118-125): Member "x" not found or not visible after argument-dependent lookup in type(contract super C). | ||||
| @ -2,5 +2,3 @@ contract C { constructor(uint) {} } | ||||
| contract D is C(D.t = 2) { | ||||
|     uint immutable t; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (52-55): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -5,5 +5,3 @@ contract C { | ||||
|             x = 1; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 6672: (88-89): Cannot write to immutable here: Immutable variables cannot be initialized inside a loop. | ||||
|  | ||||
| @ -26,4 +26,3 @@ contract C is A, B { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (499-500): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -26,4 +26,3 @@ contract C is A, B { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (503-504): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -5,5 +5,3 @@ contract C { | ||||
|         x = 4; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1574: (78-79): Immutable state variable already initialized. | ||||
|  | ||||
| @ -16,5 +16,3 @@ contract C is B { | ||||
|     function f() internal view override returns(uint) { return readX(); } | ||||
| 
 | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (202-203): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -8,5 +8,3 @@ contract B is A { | ||||
| 	uint immutable x = 2; | ||||
| 	function f() override pure internal returns (uint) { return x; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (223-224): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -3,5 +3,3 @@ contract C { | ||||
|   uint x = f(); | ||||
|   function f() internal pure returns (uint) { return t; } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (106-107): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -13,4 +13,3 @@ contract C { | ||||
| 	} | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (141-142): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -2,5 +2,3 @@ contract C { | ||||
|     uint immutable x = 0; | ||||
|     uint y = x; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 7733: (52-53): Immutable variables cannot be read before they are initialized. | ||||
|  | ||||
| @ -7,4 +7,4 @@ contract C { | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 2658: (63-70): Construction control flow ends without initializing all immutable state variables. | ||||
| // Warning 5740: (80-85): Unreachable code. | ||||
|  | ||||
| @ -0,0 +1,13 @@ | ||||
| contract C { | ||||
|     uint immutable public x = 42; | ||||
| 
 | ||||
|     function g() external view returns (uint) {} | ||||
| 
 | ||||
|     function f() public view returns (uint) { | ||||
|         return this.x(); | ||||
|     } | ||||
| 
 | ||||
|     function h() public view returns (function () external view returns (uint)) { | ||||
|         return this.x; | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,11 @@ | ||||
| contract C { | ||||
|     uint immutable public x = 42; | ||||
| 
 | ||||
|     function g() external view returns (uint) {} | ||||
| 
 | ||||
|     function f() public view { | ||||
|         this.x = this.g; | ||||
|     } | ||||
| } | ||||
| // ---- | ||||
| // TypeError 4247: (137-143): Expression has to be an lvalue. | ||||
| @ -33,9 +33,3 @@ contract B | ||||
| } | ||||
| // ==== | ||||
| // EVMVersion: >=byzantium | ||||
| // ---- | ||||
| // TypeError 4130: (108-116): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement. | ||||
| // TypeError 4130: (144-152): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement. | ||||
| // TypeError 4130: (216-224): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement. | ||||
| // TypeError 4130: (297-305): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement. | ||||
| // TypeError 4130: (357-365): Cannot write to immutable here: Immutable variables cannot be initialized inside a try/catch statement. | ||||
|  | ||||
| @ -1,5 +1,3 @@ | ||||
| contract C { | ||||
|     uint immutable x; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 2658: (0-36): Construction control flow ends without initializing all immutable state variables. | ||||
|  | ||||
| @ -16,6 +16,3 @@ contract C is B { | ||||
|     function f() internal view override returns(uint) { return readX(); } | ||||
| 
 | ||||
| } | ||||
| // ---- | ||||
| // TypeError 2658: (0-202): Construction control flow ends without initializing all immutable state variables. | ||||
| // TypeError 2658: (204-361): Construction control flow ends without initializing all immutable state variables. | ||||
|  | ||||
| @ -3,6 +3,3 @@ contract C { | ||||
|     uint immutable x = z = y = 3; | ||||
|     uint immutable y = 5; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (66-67): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
| // TypeError 1581: (62-63): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
| @ -1,5 +1,3 @@ | ||||
| contract C { | ||||
|     int immutable x = x = 5; | ||||
| } | ||||
| // ---- | ||||
| // TypeError 1581: (35-36): Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor. | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user