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: | Language Features: | ||||||
|  * Allow qualified access to events from other contracts. |  * 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: | Compiler Features: | ||||||
|  * Commandline Interface: Add ``--ast-compact-json`` output in assembler mode. |  * Commandline Interface: Add ``--ast-compact-json`` output in assembler mode. | ||||||
|  | |||||||
| @ -150,7 +150,7 @@ Modifiers | |||||||
| - ``view`` for functions: Disallows modification of state. | - ``view`` for functions: Disallows modification of state. | ||||||
| - ``payable`` for functions: Allows them to receive Ether together with a call. | - ``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. | - ``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. | - ``anonymous`` for events: Does not store event signature as topic. | ||||||
| - ``indexed`` for event parameters: Stores the parameter as topic. | - ``indexed`` for event parameters: Stores the parameter as topic. | ||||||
| - ``virtual`` for functions and modifiers: Allows the function's or modifier's | - ``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 | .. code-block:: solidity | ||||||
| 
 | 
 | ||||||
|     // SPDX-License-Identifier: GPL-3.0 |     // SPDX-License-Identifier: GPL-3.0 | ||||||
|     pragma solidity >=0.7.4; |     pragma solidity ^0.8.21; | ||||||
| 
 | 
 | ||||||
|     uint constant X = 32**22 + 8; |     uint constant X = 32**22 + 8; | ||||||
| 
 | 
 | ||||||
|     contract C { |     contract C { | ||||||
|         string constant TEXT = "abc"; |         string constant TEXT = "abc"; | ||||||
|         bytes32 constant MY_HASH = keccak256("abc"); |         bytes32 constant MY_HASH = keccak256("abc"); | ||||||
|         uint immutable decimals; |         uint immutable decimals = 18; | ||||||
|         uint immutable maxBalance; |         uint immutable maxBalance; | ||||||
|         address immutable owner = msg.sender; |         address immutable owner = msg.sender; | ||||||
| 
 | 
 | ||||||
|         constructor(uint decimals_, address ref) { |         constructor(uint decimals_, address ref) { | ||||||
|  |             if (decimals_ != 0) | ||||||
|  |                 // Immutables are only immutable when deployed. | ||||||
|  |                 // At construction time they can be assigned to any number of times. | ||||||
|                 decimals = decimals_; |                 decimals = decimals_; | ||||||
|  | 
 | ||||||
|             // Assignments to immutables can even access the environment. |             // Assignments to immutables can even access the environment. | ||||||
|             maxBalance = ref.balance; |             maxBalance = ref.balance; | ||||||
|         } |         } | ||||||
| @ -74,10 +78,29 @@ Immutable | |||||||
| ========= | ========= | ||||||
| 
 | 
 | ||||||
| Variables declared as ``immutable`` are a bit less restricted than those | Variables declared as ``immutable`` are a bit less restricted than those | ||||||
| declared as ``constant``: Immutable variables can be assigned an arbitrary | declared as ``constant``: Immutable variables can be assigned a | ||||||
| value in the constructor of the contract or at the point of their declaration. | value at construction time. | ||||||
| They can be assigned only once and can, from that point on, be read even during | The value can be changed at any time before deployment and then it becomes permanent. | ||||||
| construction time. | 
 | ||||||
|  | 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 | The contract creation code generated by the compiler will modify the | ||||||
| contract's runtime code before it is returned by replacing all references | 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 | 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 | 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>`. | 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 | This section lists the changes that are semantic-only, thus potentially | ||||||
| hiding new and different behavior in existing code. | 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 of state variable initialization has changed in case of inheritance. | ||||||
| 
 | 
 | ||||||
|   The order used to be: |   The order used to be: | ||||||
|  | |||||||
| @ -18,8 +18,6 @@ | |||||||
| 
 | 
 | ||||||
| #include <libsolidity/analysis/ImmutableValidator.h> | #include <libsolidity/analysis/ImmutableValidator.h> | ||||||
| 
 | 
 | ||||||
| #include <libsolutil/CommonData.h> |  | ||||||
| 
 |  | ||||||
| #include <range/v3/view/reverse.hpp> | #include <range/v3/view/reverse.hpp> | ||||||
| 
 | 
 | ||||||
| using namespace solidity::frontend; | using namespace solidity::frontend; | ||||||
| @ -27,275 +25,44 @@ using namespace solidity::langutil; | |||||||
| 
 | 
 | ||||||
| void ImmutableValidator::analyze() | void ImmutableValidator::analyze() | ||||||
| { | { | ||||||
| 	m_inCreationContext = true; |  | ||||||
| 
 |  | ||||||
| 	auto linearizedContracts = m_mostDerivedContract.annotation().linearizedBaseContracts | ranges::views::reverse; | 	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 (ContractDefinition const* contract: linearizedContracts) | ||||||
| 	{ | 	{ | ||||||
| 		for (VariableDeclaration const* stateVar: contract->stateVariables()) | 		for (FunctionDefinition const* function: contract->definedFunctions()) | ||||||
| 			if (stateVar->value()) | 			function->accept(*this); | ||||||
| 				m_initializedStateVariables.emplace(stateVar); |  | ||||||
| 
 | 
 | ||||||
| 		if (contract->constructor()) | 		for (ModifierDefinition const* modifier: contract->functionModifiers()) | ||||||
| 			visitCallableIfNew(*contract->constructor()); | 			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) | 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); | 	analyseVariableReference(_memberAccess.annotation().referencedDeclaration, _memberAccess); | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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), ""); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ImmutableValidator::endVisit(Identifier const& _identifier) | void ImmutableValidator::endVisit(Identifier const& _identifier) | ||||||
| { | { | ||||||
| 	if (auto const callableDef = dynamic_cast<CallableDeclaration const*>(_identifier.annotation().referencedDeclaration)) | 	analyseVariableReference(_identifier.annotation().referencedDeclaration, _identifier); | ||||||
| 		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); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void ImmutableValidator::endVisit(Return const& _return) | void ImmutableValidator::analyseVariableReference(Declaration const* _reference, Expression const& _expression) | ||||||
| { | { | ||||||
| 	if (m_currentConstructor != nullptr) | 	auto const* variable = dynamic_cast<VariableDeclaration const*>(_reference); | ||||||
| 		checkAllVariablesInitialized(_return.location()); | 	if (!variable || !variable->isStateVariable() || !variable->immutable()) | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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()) |  | ||||||
| 		return; | 		return; | ||||||
| 
 | 
 | ||||||
| 	// If this is not an ordinary assignment, we write and read at the same time.
 | 	// If this is not an ordinary assignment, we write and read at the same time.
 | ||||||
| 	bool write = _expression.annotation().willBeWrittenTo; | 	if (_expression.annotation().willBeWrittenTo) | ||||||
| 	bool read = !_expression.annotation().willBeWrittenTo || !_expression.annotation().lValueOfOrdinaryAssignment; |  | ||||||
| 	if (write) |  | ||||||
| 	{ |  | ||||||
| 		if (!m_currentConstructor) |  | ||||||
| 		m_errorReporter.typeError( | 		m_errorReporter.typeError( | ||||||
| 			1581_error, | 			1581_error, | ||||||
| 			_expression.location(), | 			_expression.location(), | ||||||
| 			"Cannot write to immutable here: Immutable variables can only be initialized inline or assigned directly in the constructor." | 			"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) |  | ||||||
| 	) |  | ||||||
| 		m_errorReporter.typeError( |  | ||||||
| 			7733_error, |  | ||||||
| 			_expression.location(), |  | ||||||
| 			"Immutable variables cannot be read before they are initialized." |  | ||||||
| 		); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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 <libsolidity/ast/ASTVisitor.h> | ||||||
| #include <liblangutil/ErrorReporter.h> | #include <liblangutil/ErrorReporter.h> | ||||||
| 
 | 
 | ||||||
| #include <memory> |  | ||||||
| 
 |  | ||||||
| namespace solidity::frontend | namespace solidity::frontend | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Validates access and initialization of immutable variables: |  * Validates access and initialization of immutable variables: | ||||||
|  * must be directly initialized in their respective c'tor or inline |  * must be directly initialized in a 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) |  | ||||||
| */ | */ | ||||||
| class ImmutableValidator: private ASTConstVisitor | class ImmutableValidator: private ASTConstVisitor | ||||||
| { | { | ||||||
| 	using CallableDeclarationSet = std::set<CallableDeclaration const*, ASTNode::CompareByID>; |  | ||||||
| 
 |  | ||||||
| public: | public: | ||||||
| 	ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): | 	ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): | ||||||
| 		m_mostDerivedContract(_contractDefinition), | 		m_mostDerivedContract(_contractDefinition), | ||||||
| @ -49,37 +39,15 @@ public: | |||||||
| 	void analyze(); | 	void analyze(); | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 	bool visit(Assignment const& _assignment); |  | ||||||
| 	bool visit(FunctionDefinition const& _functionDefinition); | 	bool visit(FunctionDefinition const& _functionDefinition); | ||||||
| 	bool visit(ModifierDefinition const& _modifierDefinition); | 	void endVisit(MemberAccess const& _memberAccess); | ||||||
| 	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(Identifier const& _identifier); | 	void endVisit(Identifier const& _identifier); | ||||||
| 	void endVisit(Return const& _return); |  | ||||||
| 
 | 
 | ||||||
| 	bool analyseCallable(CallableDeclaration const& _callableDeclaration); | 	void analyseVariableReference(Declaration const* _variableReference, Expression const& _expression); | ||||||
| 	void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression); |  | ||||||
| 
 |  | ||||||
| 	void checkAllVariablesInitialized(langutil::SourceLocation const& _location); |  | ||||||
| 
 |  | ||||||
| 	void visitCallableIfNew(Declaration const& _declaration); |  | ||||||
| 
 | 
 | ||||||
| 	ContractDefinition const& m_mostDerivedContract; | 	ContractDefinition const& m_mostDerivedContract; | ||||||
| 
 | 
 | ||||||
| 	CallableDeclarationSet m_visitedCallables; |  | ||||||
| 
 |  | ||||||
| 	std::set<VariableDeclaration const*, ASTNode::CompareByID> m_initializedStateVariables; |  | ||||||
| 	langutil::ErrorReporter& m_errorReporter; | 	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; |         x = 1; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| // ---- |  | ||||||
| // TypeError 2658: (86-93): Construction control flow ends without initializing all immutable state variables. |  | ||||||
|  | |||||||
| @ -5,5 +5,3 @@ contract C { | |||||||
|             x = 1; |             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; } |     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; |         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 {} |     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 {} |     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--; |         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; } |     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 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; } |     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++; |         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; } |     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; |     uint immutable y; | ||||||
|     constructor() B(y = 3) { } |     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) { | contract C is B(C.y = 3) { | ||||||
|     uint immutable y; |     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; |         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); |         _; 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; |         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) {} |     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; |   uint immutable t; | ||||||
|   constructor() C(t=2) {} |   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) { | contract D is C(D.t = 2) { | ||||||
|     uint immutable t; |     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; |             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; |         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(); } |     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; | 	uint immutable x = 2; | ||||||
| 	function f() override pure internal returns (uint) { return x; } | 	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(); |   uint x = f(); | ||||||
|   function f() internal pure returns (uint) { return t; } |   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 immutable x = 0; | ||||||
|     uint y = x; |     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 | // 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 { | contract C { | ||||||
|     uint immutable x; |     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(); } |     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 x = z = y = 3; | ||||||
|     uint immutable y = 5; |     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 { | contract C { | ||||||
|     int immutable x = x = 5; |     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