mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #12565 from ethereum/ice-convert-string-bytes-11677
Fix ICE when converting from calldata string to bytes
This commit is contained in:
		
						commit
						40d3223bba
					
				| @ -12,12 +12,13 @@ Compiler Features: | |||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  * Antlr Grammar: Allow builtin names in ``yulPath`` to support ``.address`` in function pointers. |  * Antlr Grammar: Allow builtin names in ``yulPath`` to support ``.address`` in function pointers. | ||||||
|  |  * Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots. | ||||||
|  |  * Code Generator: Fix ICE when doing an explicit conversion from ``string calldata`` to ``bytes``. | ||||||
|  * Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis. |  * Control Flow Graph: Perform proper virtual lookup for modifiers for uninitialized variable and unreachable code analysis. | ||||||
|  * Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables. |  * Immutables: Fix wrong error when the constructor of a base contract uses ``return`` and the parent contract contains immutable variables. | ||||||
|  * IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions. |  * IR Generator: Fix IR syntax error when copying storage arrays of structs containing functions. | ||||||
|  * Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different. |  * Natspec: Fix ICE when overriding a struct getter with a Natspec-documented return value and the name in the struct is different. | ||||||
|  * TypeChecker: Fix ICE when a constant variable declaration forward references a struct. |  * TypeChecker: Fix ICE when a constant variable declaration forward references a struct. | ||||||
|  * Code Generator: Fix ICE when accessing the members of external functions occupying more than two stack slots. |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Solc-Js: | Solc-Js: | ||||||
|  | |||||||
| @ -54,7 +54,7 @@ static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPoi | |||||||
| void CompilerUtils::initialiseFreeMemoryPointer() | void CompilerUtils::initialiseFreeMemoryPointer() | ||||||
| { | { | ||||||
| 	size_t reservedMemory = m_context.reservedMemory(); | 	size_t reservedMemory = m_context.reservedMemory(); | ||||||
| 	solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, ""); | 	solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63); | ||||||
| 	m_context << (u256(generalPurposeMemoryStart) + reservedMemory); | 	m_context << (u256(generalPurposeMemoryStart) + reservedMemory); | ||||||
| 	storeFreeMemoryPointer(); | 	storeFreeMemoryPointer(); | ||||||
| } | } | ||||||
| @ -92,7 +92,7 @@ void CompilerUtils::toSizeAfterFreeMemoryPointer() | |||||||
| 
 | 
 | ||||||
| void CompilerUtils::revertWithStringData(Type const& _argumentType) | void CompilerUtils::revertWithStringData(Type const& _argumentType) | ||||||
| { | { | ||||||
| 	solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory")), ""); | 	solAssert(_argumentType.isImplicitlyConvertibleTo(*TypeProvider::fromElementaryTypeName("string memory"))); | ||||||
| 	fetchFreeMemoryPointer(); | 	fetchFreeMemoryPointer(); | ||||||
| 	m_context << util::selectorFromSignature("Error(string)"); | 	m_context << util::selectorFromSignature("Error(string)"); | ||||||
| 	m_context << Instruction::DUP2 << Instruction::MSTORE; | 	m_context << Instruction::DUP2 << Instruction::MSTORE; | ||||||
| @ -173,9 +173,9 @@ void CompilerUtils::loadFromMemoryDynamic( | |||||||
| 
 | 
 | ||||||
| 	if (auto arrayType = dynamic_cast<ArrayType const*>(&_type)) | 	if (auto arrayType = dynamic_cast<ArrayType const*>(&_type)) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(!arrayType->isDynamicallySized(), ""); | 		solAssert(!arrayType->isDynamicallySized()); | ||||||
| 		solAssert(!_fromCalldata, ""); | 		solAssert(!_fromCalldata); | ||||||
| 		solAssert(_padToWordBoundaries, ""); | 		solAssert(_padToWordBoundaries); | ||||||
| 		if (_keepUpdatedMemoryOffset) | 		if (_keepUpdatedMemoryOffset) | ||||||
| 			m_context << arrayType->memoryDataSize() << Instruction::ADD; | 			m_context << arrayType->memoryDataSize() << Instruction::ADD; | ||||||
| 	} | 	} | ||||||
| @ -251,7 +251,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem | |||||||
| 		// Use the new Yul-based decoding function
 | 		// Use the new Yul-based decoding function
 | ||||||
| 		auto stackHeightBefore = m_context.stackHeight(); | 		auto stackHeightBefore = m_context.stackHeight(); | ||||||
| 		abiDecodeV2(_typeParameters, _fromMemory); | 		abiDecodeV2(_typeParameters, _fromMemory); | ||||||
| 		solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); | 		solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -290,7 +290,7 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem | |||||||
| 				); | 				); | ||||||
| 				// @todo If base type is an array or struct, it is still calldata-style encoded, so
 | 				// @todo If base type is an array or struct, it is still calldata-style encoded, so
 | ||||||
| 				// we would have to convert it like below.
 | 				// we would have to convert it like below.
 | ||||||
| 				solAssert(arrayType.location() == DataLocation::Memory, ""); | 				solAssert(arrayType.location() == DataLocation::Memory); | ||||||
| 				if (arrayType.isDynamicallySized()) | 				if (arrayType.isDynamicallySized()) | ||||||
| 				{ | 				{ | ||||||
| 					// compute data pointer
 | 					// compute data pointer
 | ||||||
| @ -430,7 +430,7 @@ void CompilerUtils::encodeToMemory( | |||||||
| 	// stack: <v1> <v2> ... <vn> <mem>
 | 	// stack: <v1> <v2> ... <vn> <mem>
 | ||||||
| 	bool const encoderV2 = m_context.useABICoderV2(); | 	bool const encoderV2 = m_context.useABICoderV2(); | ||||||
| 	TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; | 	TypePointers targetTypes = _targetTypes.empty() ? _givenTypes : _targetTypes; | ||||||
| 	solAssert(targetTypes.size() == _givenTypes.size(), ""); | 	solAssert(targetTypes.size() == _givenTypes.size()); | ||||||
| 	for (Type const*& t: targetTypes) | 	for (Type const*& t: targetTypes) | ||||||
| 	{ | 	{ | ||||||
| 		Type const* tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries); | 		Type const* tEncoding = t->fullEncodingType(_encodeAsLibraryTypes, encoderV2, !_padToWordBoundaries); | ||||||
| @ -449,7 +449,7 @@ void CompilerUtils::encodeToMemory( | |||||||
| 		); | 		); | ||||||
| 		auto stackHeightBefore = m_context.stackHeight(); | 		auto stackHeightBefore = m_context.stackHeight(); | ||||||
| 		abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries); | 		abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes, _padToWordBoundaries); | ||||||
| 		solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); | 		solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes)); | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -489,8 +489,8 @@ void CompilerUtils::encodeToMemory( | |||||||
| 			{ | 			{ | ||||||
| 				// special case: convert storage reference type to value type - this is only
 | 				// special case: convert storage reference type to value type - this is only
 | ||||||
| 				// possible for library calls where we just forward the storage reference
 | 				// possible for library calls where we just forward the storage reference
 | ||||||
| 				solAssert(_encodeAsLibraryTypes, ""); | 				solAssert(_encodeAsLibraryTypes); | ||||||
| 				solAssert(_givenTypes[i]->sizeOnStack() == 1, ""); | 				solAssert(_givenTypes[i]->sizeOnStack() == 1); | ||||||
| 			} | 			} | ||||||
| 			else if ( | 			else if ( | ||||||
| 				_givenTypes[i]->dataStoredIn(DataLocation::Storage) || | 				_givenTypes[i]->dataStoredIn(DataLocation::Storage) || | ||||||
| @ -638,7 +638,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) | |||||||
| { | { | ||||||
| 	if (_type.baseType()->hasSimpleZeroValueInMemory()) | 	if (_type.baseType()->hasSimpleZeroValueInMemory()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(_type.baseType()->isValueType(), ""); | 		solAssert(_type.baseType()->isValueType()); | ||||||
| 		Whiskers templ(R"({ | 		Whiskers templ(R"({ | ||||||
| 			let size := mul(length, <element_size>) | 			let size := mul(length, <element_size>) | ||||||
| 			// cheap way of zero-initializing a memory range
 | 			// cheap way of zero-initializing a memory range
 | ||||||
| @ -774,9 +774,9 @@ void CompilerUtils::convertType( | |||||||
| 
 | 
 | ||||||
| 	if (stackTypeCategory == Type::Category::UserDefinedValueType) | 	if (stackTypeCategory == Type::Category::UserDefinedValueType) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(_cleanupNeeded, ""); | 		solAssert(_cleanupNeeded); | ||||||
| 		auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack); | 		auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_typeOnStack); | ||||||
| 		solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType(), ""); | 		solAssert(_typeOnStack == _targetType || _targetType == userDefined.underlyingType()); | ||||||
| 		return convertType( | 		return convertType( | ||||||
| 			userDefined.underlyingType(), | 			userDefined.underlyingType(), | ||||||
| 			_targetType, | 			_targetType, | ||||||
| @ -787,9 +787,9 @@ void CompilerUtils::convertType( | |||||||
| 	} | 	} | ||||||
| 	if (targetTypeCategory == Type::Category::UserDefinedValueType) | 	if (targetTypeCategory == Type::Category::UserDefinedValueType) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(_cleanupNeeded, ""); | 		solAssert(_cleanupNeeded); | ||||||
| 		auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType); | 		auto& userDefined = dynamic_cast<UserDefinedValueType const&>(_targetType); | ||||||
| 		solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType()), ""); | 		solAssert(_typeOnStack.isImplicitlyConvertibleTo(userDefined.underlyingType())); | ||||||
| 		return convertType( | 		return convertType( | ||||||
| 			_typeOnStack, | 			_typeOnStack, | ||||||
| 			userDefined.underlyingType(), | 			userDefined.underlyingType(), | ||||||
| @ -829,7 +829,7 @@ void CompilerUtils::convertType( | |||||||
| 		} | 		} | ||||||
| 		else if (targetTypeCategory == Type::Category::Address) | 		else if (targetTypeCategory == Type::Category::Address) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(typeOnStack.numBytes() * 8 == 160, ""); | 			solAssert(typeOnStack.numBytes() * 8 == 160); | ||||||
| 			rightShiftNumberOnStack(256 - 160); | 			rightShiftNumberOnStack(256 - 160); | ||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| @ -849,7 +849,7 @@ void CompilerUtils::convertType( | |||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	case Type::Category::Enum: | 	case Type::Category::Enum: | ||||||
| 		solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer, ""); | 		solAssert(_targetType == _typeOnStack || targetTypeCategory == Type::Category::Integer); | ||||||
| 		if (enumOverflowCheckPending) | 		if (enumOverflowCheckPending) | ||||||
| 		{ | 		{ | ||||||
| 			EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack); | 			EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack); | ||||||
| @ -885,13 +885,13 @@ void CompilerUtils::convertType( | |||||||
| 					cleanHigherOrderBits(*typeOnStack); | 					cleanHigherOrderBits(*typeOnStack); | ||||||
| 			} | 			} | ||||||
| 			else if (stackTypeCategory == Type::Category::Address) | 			else if (stackTypeCategory == Type::Category::Address) | ||||||
| 				solAssert(targetBytesType.numBytes() * 8 == 160, ""); | 				solAssert(targetBytesType.numBytes() * 8 == 160); | ||||||
| 			leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); | 			leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8); | ||||||
| 		} | 		} | ||||||
| 		else if (targetTypeCategory == Type::Category::Enum) | 		else if (targetTypeCategory == Type::Category::Enum) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested."); | 			solAssert(stackTypeCategory != Type::Category::Address, "Invalid conversion to EnumType requested."); | ||||||
| 			solAssert(_typeOnStack.mobileType(), ""); | 			solAssert(_typeOnStack.mobileType()); | ||||||
| 			// just clean
 | 			// just clean
 | ||||||
| 			convertType(_typeOnStack, *_typeOnStack.mobileType(), true); | 			convertType(_typeOnStack, *_typeOnStack.mobileType(), true); | ||||||
| 			EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType); | 			EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType); | ||||||
| @ -964,13 +964,13 @@ void CompilerUtils::convertType( | |||||||
| 		if (targetTypeCategory == Type::Category::FixedBytes) | 		if (targetTypeCategory == Type::Category::FixedBytes) | ||||||
| 		{ | 		{ | ||||||
| 			unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes(); | 			unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_targetType).numBytes(); | ||||||
| 			solAssert(data.size() <= 32, ""); | 			solAssert(data.size() <= 32); | ||||||
| 			m_context << (u256(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes)))); | 			m_context << (u256(h256(data, h256::AlignLeft)) & (~(u256(-1) >> (8 * numBytes)))); | ||||||
| 		} | 		} | ||||||
| 		else if (targetTypeCategory == Type::Category::Array) | 		else if (targetTypeCategory == Type::Category::Array) | ||||||
| 		{ | 		{ | ||||||
| 			auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType); | 			auto const& arrayType = dynamic_cast<ArrayType const&>(_targetType); | ||||||
| 			solAssert(arrayType.isByteArray(), ""); | 			solAssert(arrayType.isByteArray()); | ||||||
| 			size_t storageSize = 32 + ((data.size() + 31) / 32) * 32; | 			size_t storageSize = 32 + ((data.size() + 31) / 32) * 32; | ||||||
| 			allocateMemory(storageSize); | 			allocateMemory(storageSize); | ||||||
| 			// stack: mempos
 | 			// stack: mempos
 | ||||||
| @ -995,10 +995,10 @@ void CompilerUtils::convertType( | |||||||
| 				typeOnStack.isByteArray() && !typeOnStack.isString(), | 				typeOnStack.isByteArray() && !typeOnStack.isString(), | ||||||
| 				"Array types other than bytes not convertible to bytesNN." | 				"Array types other than bytes not convertible to bytesNN." | ||||||
| 			); | 			); | ||||||
| 			solAssert(typeOnStack.isDynamicallySized(), ""); | 			solAssert(typeOnStack.isDynamicallySized()); | ||||||
| 
 | 
 | ||||||
| 			bool fromCalldata = typeOnStack.dataStoredIn(DataLocation::CallData); | 			bool fromCalldata = typeOnStack.dataStoredIn(DataLocation::CallData); | ||||||
| 			solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1), ""); | 			solAssert(typeOnStack.sizeOnStack() == (fromCalldata ? 2 : 1)); | ||||||
| 			if (fromCalldata) | 			if (fromCalldata) | ||||||
| 				m_context << Instruction::SWAP1; | 				m_context << Instruction::SWAP1; | ||||||
| 
 | 
 | ||||||
| @ -1012,7 +1012,7 @@ void CompilerUtils::convertType( | |||||||
| 			); | 			); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		solAssert(targetTypeCategory == stackTypeCategory, ""); | 		solAssert(targetTypeCategory == stackTypeCategory); | ||||||
| 		auto const& targetType = dynamic_cast<ArrayType const&>(_targetType); | 		auto const& targetType = dynamic_cast<ArrayType const&>(_targetType); | ||||||
| 		switch (targetType.location()) | 		switch (targetType.location()) | ||||||
| 		{ | 		{ | ||||||
| @ -1034,9 +1034,9 @@ void CompilerUtils::convertType( | |||||||
| 					typeOnStack.baseType()->isDynamicallyEncoded() | 					typeOnStack.baseType()->isDynamicallyEncoded() | ||||||
| 				) | 				) | ||||||
| 				{ | 				{ | ||||||
| 					solAssert(m_context.useABICoderV2(), ""); | 					solAssert(m_context.useABICoderV2()); | ||||||
| 					// stack: offset length(optional in case of dynamically sized array)
 | 					// stack: offset length(optional in case of dynamically sized array)
 | ||||||
| 					solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1), ""); | 					solAssert(typeOnStack.sizeOnStack() == (typeOnStack.isDynamicallySized() ? 2 : 1)); | ||||||
| 					if (typeOnStack.isDynamicallySized()) | 					if (typeOnStack.isDynamicallySized()) | ||||||
| 						m_context << Instruction::SWAP1; | 						m_context << Instruction::SWAP1; | ||||||
| 
 | 
 | ||||||
| @ -1122,9 +1122,9 @@ void CompilerUtils::convertType( | |||||||
| 				typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(), | 				typeOnStack.arrayType().isByteArray() && !typeOnStack.arrayType().isString(), | ||||||
| 				"Array types other than bytes not convertible to bytesNN." | 				"Array types other than bytes not convertible to bytesNN." | ||||||
| 			); | 			); | ||||||
| 			solAssert(typeOnStack.isDynamicallySized(), ""); | 			solAssert(typeOnStack.isDynamicallySized()); | ||||||
| 			solAssert(typeOnStack.dataStoredIn(DataLocation::CallData), ""); | 			solAssert(typeOnStack.dataStoredIn(DataLocation::CallData)); | ||||||
| 			solAssert(typeOnStack.sizeOnStack() == 2, ""); | 			solAssert(typeOnStack.sizeOnStack() == 2); | ||||||
| 
 | 
 | ||||||
| 			m_context << Instruction::SWAP1; | 			m_context << Instruction::SWAP1; | ||||||
| 			m_context.callYulFunction( | 			m_context.callYulFunction( | ||||||
| @ -1138,14 +1138,16 @@ void CompilerUtils::convertType( | |||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		solAssert(_targetType.category() == Type::Category::Array, ""); | 		solAssert(_targetType.category() == Type::Category::Array); | ||||||
| 		auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType); | 		auto const& targetArrayType = dynamic_cast<ArrayType const&>(_targetType); | ||||||
| 		solAssert(typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType), ""); | 		solAssert( | ||||||
|  | 			typeOnStack.arrayType().isImplicitlyConvertibleTo(targetArrayType) || | ||||||
|  | 			(typeOnStack.arrayType().isByteArray() && targetArrayType.isByteArray()) | ||||||
|  | 		); | ||||||
| 		solAssert( | 		solAssert( | ||||||
| 			typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) && | 			typeOnStack.arrayType().dataStoredIn(DataLocation::CallData) && | ||||||
| 			typeOnStack.arrayType().isDynamicallySized() && | 			typeOnStack.arrayType().isDynamicallySized() && | ||||||
| 			!typeOnStack.arrayType().baseType()->isDynamicallyEncoded(), | 			!typeOnStack.arrayType().baseType()->isDynamicallyEncoded() | ||||||
| 			"" |  | ||||||
| 		); | 		); | ||||||
| 		if (!_targetType.dataStoredIn(DataLocation::CallData)) | 		if (!_targetType.dataStoredIn(DataLocation::CallData)) | ||||||
| 			return convertType(typeOnStack.arrayType(), _targetType); | 			return convertType(typeOnStack.arrayType(), _targetType); | ||||||
| @ -1153,7 +1155,7 @@ void CompilerUtils::convertType( | |||||||
| 	} | 	} | ||||||
| 	case Type::Category::Struct: | 	case Type::Category::Struct: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(targetTypeCategory == stackTypeCategory, ""); | 		solAssert(targetTypeCategory == stackTypeCategory); | ||||||
| 		auto& targetType = dynamic_cast<StructType const&>(_targetType); | 		auto& targetType = dynamic_cast<StructType const&>(_targetType); | ||||||
| 		auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack); | 		auto& typeOnStack = dynamic_cast<StructType const&>(_typeOnStack); | ||||||
| 		switch (targetType.location()) | 		switch (targetType.location()) | ||||||
| @ -1182,7 +1184,7 @@ void CompilerUtils::convertType( | |||||||
| 					// stack: <memory ptr> <source ref> <memory ptr>
 | 					// stack: <memory ptr> <source ref> <memory ptr>
 | ||||||
| 					for (auto const& member: typeOnStack->members(nullptr)) | 					for (auto const& member: typeOnStack->members(nullptr)) | ||||||
| 					{ | 					{ | ||||||
| 						solAssert(!member.type->containsNestedMapping(), ""); | 						solAssert(!member.type->containsNestedMapping()); | ||||||
| 						pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name); | 						pair<u256, unsigned> const& offsets = typeOnStack->storageOffsetsOfMember(member.name); | ||||||
| 						_context << offsets.first << Instruction::DUP3 << Instruction::ADD; | 						_context << offsets.first << Instruction::DUP3 << Instruction::ADD; | ||||||
| 						_context << u256(offsets.second); | 						_context << u256(offsets.second); | ||||||
| @ -1209,7 +1211,7 @@ void CompilerUtils::convertType( | |||||||
| 			{ | 			{ | ||||||
| 				if (typeOnStack.isDynamicallyEncoded()) | 				if (typeOnStack.isDynamicallyEncoded()) | ||||||
| 				{ | 				{ | ||||||
| 					solAssert(m_context.useABICoderV2(), ""); | 					solAssert(m_context.useABICoderV2()); | ||||||
| 					m_context.callYulFunction( | 					m_context.callYulFunction( | ||||||
| 						m_context.utilFunctions().conversionFunction(typeOnStack, targetType), | 						m_context.utilFunctions().conversionFunction(typeOnStack, targetType), | ||||||
| 						1, | 						1, | ||||||
| @ -1231,7 +1233,7 @@ void CompilerUtils::convertType( | |||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
| 		case DataLocation::CallData: | 		case DataLocation::CallData: | ||||||
| 			solAssert(_typeOnStack == _targetType, ""); | 			solAssert(_typeOnStack == _targetType); | ||||||
| 			// nothing to do
 | 			// nothing to do
 | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| @ -1241,7 +1243,7 @@ void CompilerUtils::convertType( | |||||||
| 	{ | 	{ | ||||||
| 		TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack); | 		TupleType const& sourceTuple = dynamic_cast<TupleType const&>(_typeOnStack); | ||||||
| 		TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType); | 		TupleType const& targetTuple = dynamic_cast<TupleType const&>(_targetType); | ||||||
| 		solAssert(targetTuple.components().size() == sourceTuple.components().size(), ""); | 		solAssert(targetTuple.components().size() == sourceTuple.components().size()); | ||||||
| 		unsigned depth = sourceTuple.sizeOnStack(); | 		unsigned depth = sourceTuple.sizeOnStack(); | ||||||
| 		for (size_t i = 0; i < sourceTuple.components().size(); ++i) | 		for (size_t i = 0; i < sourceTuple.components().size(); ++i) | ||||||
| 		{ | 		{ | ||||||
| @ -1249,7 +1251,7 @@ void CompilerUtils::convertType( | |||||||
| 			Type const* targetType = targetTuple.components()[i]; | 			Type const* targetType = targetTuple.components()[i]; | ||||||
| 			if (!sourceType) | 			if (!sourceType) | ||||||
| 			{ | 			{ | ||||||
| 				solAssert(!targetType, ""); | 				solAssert(!targetType); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			unsigned sourceSize = sourceType->sizeOnStack(); | 			unsigned sourceSize = sourceType->sizeOnStack(); | ||||||
| @ -1291,7 +1293,7 @@ void CompilerUtils::convertType( | |||||||
| 		break; | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		// we used to allow conversions from function to address
 | 		// we used to allow conversions from function to address
 | ||||||
| 		solAssert(!(stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address), ""); | 		solAssert(!(stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Address)); | ||||||
| 		if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function) | 		if (stackTypeCategory == Type::Category::Function && targetTypeCategory == Type::Category::Function) | ||||||
| 		{ | 		{ | ||||||
| 			FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); | 			FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack); | ||||||
| @ -1348,14 +1350,14 @@ void CompilerUtils::pushZeroValue(Type const& _type) | |||||||
| 	} | 	} | ||||||
| 	if (referenceType->location() == DataLocation::CallData) | 	if (referenceType->location() == DataLocation::CallData) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2, ""); | 		solAssert(referenceType->sizeOnStack() == 1 || referenceType->sizeOnStack() == 2); | ||||||
| 		m_context << Instruction::CALLDATASIZE; | 		m_context << Instruction::CALLDATASIZE; | ||||||
| 		if (referenceType->sizeOnStack() == 2) | 		if (referenceType->sizeOnStack() == 2) | ||||||
| 			m_context << 0; | 			m_context << 0; | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	solAssert(referenceType->location() == DataLocation::Memory, ""); | 	solAssert(referenceType->location() == DataLocation::Memory); | ||||||
| 	if (auto arrayType = dynamic_cast<ArrayType const*>(&_type)) | 	if (auto arrayType = dynamic_cast<ArrayType const*>(&_type)) | ||||||
| 		if (arrayType->isDynamicallySized()) | 		if (arrayType->isDynamicallySized()) | ||||||
| 		{ | 		{ | ||||||
| @ -1383,7 +1385,7 @@ void CompilerUtils::pushZeroValue(Type const& _type) | |||||||
| 				} | 				} | ||||||
| 			else if (auto arrayType = dynamic_cast<ArrayType const*>(type)) | 			else if (auto arrayType = dynamic_cast<ArrayType const*>(type)) | ||||||
| 			{ | 			{ | ||||||
| 				solAssert(!arrayType->isDynamicallySized(), ""); | 				solAssert(!arrayType->isDynamicallySized()); | ||||||
| 				if (arrayType->length() > 0) | 				if (arrayType->length() > 0) | ||||||
| 				{ | 				{ | ||||||
| 					_context << arrayType->length() << Instruction::SWAP1; | 					_context << arrayType->length() << Instruction::SWAP1; | ||||||
| @ -1483,7 +1485,7 @@ void CompilerUtils::popStackSlots(size_t _amount) | |||||||
| 
 | 
 | ||||||
| void CompilerUtils::popAndJump(unsigned _toHeight, evmasm::AssemblyItem const& _jumpTo) | void CompilerUtils::popAndJump(unsigned _toHeight, evmasm::AssemblyItem const& _jumpTo) | ||||||
| { | { | ||||||
| 	solAssert(m_context.stackHeight() >= _toHeight, ""); | 	solAssert(m_context.stackHeight() >= _toHeight); | ||||||
| 	unsigned amount = m_context.stackHeight() - _toHeight; | 	unsigned amount = m_context.stackHeight() - _toHeight; | ||||||
| 	popStackSlots(amount); | 	popStackSlots(amount); | ||||||
| 	m_context.appendJumpTo(_jumpTo); | 	m_context.appendJumpTo(_jumpTo); | ||||||
| @ -1551,7 +1553,7 @@ void CompilerUtils::storeStringData(bytesConstRef _data) | |||||||
| 
 | 
 | ||||||
| unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords) | unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords) | ||||||
| { | { | ||||||
| 	solAssert(_type.isValueType(), ""); | 	solAssert(_type.isValueType()); | ||||||
| 	Type const* type = &_type; | 	Type const* type = &_type; | ||||||
| 	if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type)) | 	if (auto const* userDefined = dynamic_cast<UserDefinedValueType const*>(type)) | ||||||
| 		type = &userDefined->underlyingType(); | 		type = &userDefined->underlyingType(); | ||||||
| @ -1603,7 +1605,7 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack) | |||||||
| 
 | 
 | ||||||
| void CompilerUtils::leftShiftNumberOnStack(unsigned _bits) | void CompilerUtils::leftShiftNumberOnStack(unsigned _bits) | ||||||
| { | { | ||||||
| 	solAssert(_bits < 256, ""); | 	solAssert(_bits < 256); | ||||||
| 	if (m_context.evmVersion().hasBitwiseShifting()) | 	if (m_context.evmVersion().hasBitwiseShifting()) | ||||||
| 		m_context << _bits << Instruction::SHL; | 		m_context << _bits << Instruction::SHL; | ||||||
| 	else | 	else | ||||||
| @ -1612,7 +1614,7 @@ void CompilerUtils::leftShiftNumberOnStack(unsigned _bits) | |||||||
| 
 | 
 | ||||||
| void CompilerUtils::rightShiftNumberOnStack(unsigned _bits) | void CompilerUtils::rightShiftNumberOnStack(unsigned _bits) | ||||||
| { | { | ||||||
| 	solAssert(_bits < 256, ""); | 	solAssert(_bits < 256); | ||||||
| 	// NOTE: If we add signed right shift, SAR rounds differently than SDIV
 | 	// NOTE: If we add signed right shift, SAR rounds differently than SDIV
 | ||||||
| 	if (m_context.evmVersion().hasBitwiseShifting()) | 	if (m_context.evmVersion().hasBitwiseShifting()) | ||||||
| 		m_context << _bits << Instruction::SHR; | 		m_context << _bits << Instruction::SHR; | ||||||
| @ -1627,7 +1629,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords, | |||||||
| 		"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")." | 		"Memory store of types with stack size != 1 not allowed (Type: " + _type.toString(true) + ")." | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	solAssert(!_type.isDynamicallyEncoded(), ""); | 	solAssert(!_type.isDynamicallyEncoded()); | ||||||
| 
 | 
 | ||||||
| 	unsigned numBytes = _type.calldataEncodedSize(_padToWords); | 	unsigned numBytes = _type.calldataEncodedSize(_padToWords); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3219,15 +3219,17 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) | |||||||
| 			solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN."); | 			solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN."); | ||||||
| 			return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to)); | 			return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to)); | ||||||
| 		} | 		} | ||||||
| 		solAssert(_to.category() == Type::Category::Array, ""); | 		solAssert(_to.category() == Type::Category::Array); | ||||||
| 		auto const& targetType = dynamic_cast<ArrayType const&>(_to); | 		auto const& targetType = dynamic_cast<ArrayType const&>(_to); | ||||||
| 
 | 
 | ||||||
| 		solAssert(fromType.arrayType().isImplicitlyConvertibleTo(targetType), ""); | 		solAssert( | ||||||
|  | 			fromType.arrayType().isImplicitlyConvertibleTo(targetType) || | ||||||
|  | 			(fromType.arrayType().isByteArray() && targetType.isByteArray()) | ||||||
|  | 		); | ||||||
| 		solAssert( | 		solAssert( | ||||||
| 			fromType.arrayType().dataStoredIn(DataLocation::CallData) && | 			fromType.arrayType().dataStoredIn(DataLocation::CallData) && | ||||||
| 			fromType.arrayType().isDynamicallySized() && | 			fromType.arrayType().isDynamicallySized() && | ||||||
| 			!fromType.arrayType().baseType()->isDynamicallyEncoded(), | 			!fromType.arrayType().baseType()->isDynamicallyEncoded() | ||||||
| 			"" |  | ||||||
| 		); | 		); | ||||||
| 
 | 
 | ||||||
| 		if (!targetType.dataStoredIn(DataLocation::CallData)) | 		if (!targetType.dataStoredIn(DataLocation::CallData)) | ||||||
|  | |||||||
| @ -95,7 +95,7 @@ struct CopyTranslate: public yul::ASTCopier | |||||||
| 			return ASTCopier::translate(_identifier); | 			return ASTCopier::translate(_identifier); | ||||||
| 
 | 
 | ||||||
| 		yul::Expression translated = translateReference(_identifier); | 		yul::Expression translated = translateReference(_identifier); | ||||||
| 		solAssert(holds_alternative<yul::Identifier>(translated), ""); | 		solAssert(holds_alternative<yul::Identifier>(translated)); | ||||||
| 		return get<yul::Identifier>(std::move(translated)); | 		return get<yul::Identifier>(std::move(translated)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @ -115,14 +115,14 @@ private: | |||||||
| 		if (suffix.empty() && varDecl->isLocalVariable()) | 		if (suffix.empty() && varDecl->isLocalVariable()) | ||||||
| 		{ | 		{ | ||||||
| 			auto const& var = m_context.localVariable(*varDecl); | 			auto const& var = m_context.localVariable(*varDecl); | ||||||
| 			solAssert(var.type().sizeOnStack() == 1, ""); | 			solAssert(var.type().sizeOnStack() == 1); | ||||||
| 
 | 
 | ||||||
| 			value = var.commaSeparatedList(); | 			value = var.commaSeparatedList(); | ||||||
| 		} | 		} | ||||||
| 		else if (varDecl->isConstant()) | 		else if (varDecl->isConstant()) | ||||||
| 		{ | 		{ | ||||||
| 			VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl); | 			VariableDeclaration const* variable = rootConstVariableDeclaration(*varDecl); | ||||||
| 			solAssert(variable, ""); | 			solAssert(variable); | ||||||
| 
 | 
 | ||||||
| 			if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) | 			if (variable->value()->annotation().type->category() == Type::Category::RationalNumber) | ||||||
| 			{ | 			{ | ||||||
| @ -130,7 +130,7 @@ private: | |||||||
| 				if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type())) | 				if (auto const* bytesType = dynamic_cast<FixedBytesType const*>(variable->type())) | ||||||
| 					intValue <<= 256 - 8 * bytesType->numBytes(); | 					intValue <<= 256 - 8 * bytesType->numBytes(); | ||||||
| 				else | 				else | ||||||
| 					solAssert(variable->type()->category() == Type::Category::Integer, ""); | 					solAssert(variable->type()->category() == Type::Category::Integer); | ||||||
| 				value = intValue.str(); | 				value = intValue.str(); | ||||||
| 			} | 			} | ||||||
| 			else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get())) | 			else if (auto const* literal = dynamic_cast<Literal const*>(variable->value().get())) | ||||||
| @ -141,20 +141,20 @@ private: | |||||||
| 				{ | 				{ | ||||||
| 				case Type::Category::Bool: | 				case Type::Category::Bool: | ||||||
| 				case Type::Category::Address: | 				case Type::Category::Address: | ||||||
| 					solAssert(type->category() == variable->annotation().type->category(), ""); | 					solAssert(type->category() == variable->annotation().type->category()); | ||||||
| 					value = toCompactHexWithPrefix(type->literalValue(literal)); | 					value = toCompactHexWithPrefix(type->literalValue(literal)); | ||||||
| 					break; | 					break; | ||||||
| 				case Type::Category::StringLiteral: | 				case Type::Category::StringLiteral: | ||||||
| 				{ | 				{ | ||||||
| 					auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type); | 					auto const& stringLiteral = dynamic_cast<StringLiteralType const&>(*type); | ||||||
| 					solAssert(variable->type()->category() == Type::Category::FixedBytes, ""); | 					solAssert(variable->type()->category() == Type::Category::FixedBytes); | ||||||
| 					unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes(); | 					unsigned const numBytes = dynamic_cast<FixedBytesType const&>(*variable->type()).numBytes(); | ||||||
| 					solAssert(stringLiteral.value().size() <= numBytes, ""); | 					solAssert(stringLiteral.value().size() <= numBytes); | ||||||
| 					value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft))); | 					value = formatNumber(u256(h256(stringLiteral.value(), h256::AlignLeft))); | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
| 				default: | 				default: | ||||||
| 					solAssert(false, ""); | 					solAssert(false); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			else | 			else | ||||||
| @ -167,25 +167,25 @@ private: | |||||||
| 			else if (suffix == "offset") | 			else if (suffix == "offset") | ||||||
| 				value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second); | 				value = to_string(m_context.storageLocationOfStateVariable(*varDecl).second); | ||||||
| 			else | 			else | ||||||
| 				solAssert(false, ""); | 				solAssert(false); | ||||||
| 		} | 		} | ||||||
| 		else if (varDecl->type()->dataStoredIn(DataLocation::Storage)) | 		else if (varDecl->type()->dataStoredIn(DataLocation::Storage)) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(suffix == "slot" || suffix == "offset", ""); | 			solAssert(suffix == "slot" || suffix == "offset"); | ||||||
| 			solAssert(varDecl->isLocalVariable(), ""); | 			solAssert(varDecl->isLocalVariable()); | ||||||
| 			if (suffix == "slot") | 			if (suffix == "slot") | ||||||
| 				value = IRVariable{*varDecl}.part("slot").name(); | 				value = IRVariable{*varDecl}.part("slot").name(); | ||||||
| 			else if (varDecl->type()->isValueType()) | 			else if (varDecl->type()->isValueType()) | ||||||
| 				value = IRVariable{*varDecl}.part("offset").name(); | 				value = IRVariable{*varDecl}.part("offset").name(); | ||||||
| 			else | 			else | ||||||
| 			{ | 			{ | ||||||
| 				solAssert(!IRVariable{*varDecl}.hasPart("offset"), ""); | 				solAssert(!IRVariable{*varDecl}.hasPart("offset")); | ||||||
| 				value = "0"; | 				value = "0"; | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 		else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) | 		else if (varDecl->type()->dataStoredIn(DataLocation::CallData)) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(suffix == "offset" || suffix == "length", ""); | 			solAssert(suffix == "offset" || suffix == "length"); | ||||||
| 			value = IRVariable{*varDecl}.part(suffix).name(); | 			value = IRVariable{*varDecl}.part(suffix).name(); | ||||||
| 		} | 		} | ||||||
| 		else if ( | 		else if ( | ||||||
| @ -193,15 +193,15 @@ private: | |||||||
| 			functionType && functionType->kind() == FunctionType::Kind::External | 			functionType && functionType->kind() == FunctionType::Kind::External | ||||||
| 		) | 		) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(suffix == "selector" || suffix == "address", ""); | 			solAssert(suffix == "selector" || suffix == "address"); | ||||||
| 			solAssert(varDecl->type()->sizeOnStack() == 2, ""); | 			solAssert(varDecl->type()->sizeOnStack() == 2); | ||||||
| 			if (suffix == "selector") | 			if (suffix == "selector") | ||||||
| 				value = IRVariable{*varDecl}.part("functionSelector").name(); | 				value = IRVariable{*varDecl}.part("functionSelector").name(); | ||||||
| 			else | 			else | ||||||
| 				value = IRVariable{*varDecl}.part("address").name(); | 				value = IRVariable{*varDecl}.part("address").name(); | ||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| 			solAssert(false, ""); | 			solAssert(false); | ||||||
| 
 | 
 | ||||||
| 		if (isdigit(value.front())) | 		if (isdigit(value.front())) | ||||||
| 			return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}}; | 			return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}}; | ||||||
| @ -268,7 +268,7 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va | |||||||
| 		setLocation(_varDecl); | 		setLocation(_varDecl); | ||||||
| 
 | 
 | ||||||
| 		solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); | 		solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); | ||||||
| 		solAssert(!_varDecl.isConstant(), ""); | 		solAssert(!_varDecl.isConstant()); | ||||||
| 		if (!_varDecl.value()) | 		if (!_varDecl.value()) | ||||||
| 			return; | 			return; | ||||||
| 
 | 
 | ||||||
| @ -355,7 +355,7 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const | |||||||
| 			templ("sourceLocationComment", dispenseLocationComment(_constant, m_context)); | 			templ("sourceLocationComment", dispenseLocationComment(_constant, m_context)); | ||||||
| 			templ("functionName", functionName); | 			templ("functionName", functionName); | ||||||
| 			IRGeneratorForStatements generator(m_context, m_utils); | 			IRGeneratorForStatements generator(m_context, m_utils); | ||||||
| 			solAssert(_constant.value(), ""); | 			solAssert(_constant.value()); | ||||||
| 			Type const& constantType = *_constant.type(); | 			Type const& constantType = *_constant.type(); | ||||||
| 			templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); | 			templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); | ||||||
| 			templ("code", generator.code()); | 			templ("code", generator.code()); | ||||||
| @ -386,7 +386,7 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var | |||||||
| 			for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i) | 			for (size_t i = 0; i < _varDeclStatement.declarations().size(); ++i) | ||||||
| 				if (auto const& decl = _varDeclStatement.declarations()[i]) | 				if (auto const& decl = _varDeclStatement.declarations()[i]) | ||||||
| 				{ | 				{ | ||||||
| 					solAssert(tupleType->components()[i], ""); | 					solAssert(tupleType->components()[i]); | ||||||
| 					define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i)); | 					define(m_context.addLocalVariable(*decl), IRVariable(*expression).tupleComponent(i)); | ||||||
| 				} | 				} | ||||||
| 		} | 		} | ||||||
| @ -443,7 +443,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | |||||||
| 		TokenTraits::AssignmentToBinaryOp(assignmentOperator); | 		TokenTraits::AssignmentToBinaryOp(assignmentOperator); | ||||||
| 
 | 
 | ||||||
| 	if (TokenTraits::isShiftOp(binaryOperator)) | 	if (TokenTraits::isShiftOp(binaryOperator)) | ||||||
| 		solAssert(type(_assignment.rightHandSide()).mobileType(), ""); | 		solAssert(type(_assignment.rightHandSide()).mobileType()); | ||||||
| 	IRVariable value = | 	IRVariable value = | ||||||
| 		type(_assignment.leftHandSide()).isValueType() ? | 		type(_assignment.leftHandSide()).isValueType() ? | ||||||
| 		convert( | 		convert( | ||||||
| @ -460,11 +460,11 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | |||||||
| 	if (assignmentOperator != Token::Assign) | 	if (assignmentOperator != Token::Assign) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); | 		solAssert(type(_assignment.leftHandSide()).isValueType(), "Compound operators only available for value types."); | ||||||
| 		solAssert(binaryOperator != Token::Exp, ""); | 		solAssert(binaryOperator != Token::Exp); | ||||||
| 		solAssert(type(_assignment) == type(_assignment.leftHandSide()), ""); | 		solAssert(type(_assignment) == type(_assignment.leftHandSide())); | ||||||
| 
 | 
 | ||||||
| 		IRVariable leftIntermediate = readFromLValue(*m_currentLValue); | 		IRVariable leftIntermediate = readFromLValue(*m_currentLValue); | ||||||
| 		solAssert(type(_assignment) == leftIntermediate.type(), ""); | 		solAssert(type(_assignment) == leftIntermediate.type()); | ||||||
| 
 | 
 | ||||||
| 		define(_assignment) << ( | 		define(_assignment) << ( | ||||||
| 			TokenTraits::isShiftOp(binaryOperator) ? | 			TokenTraits::isShiftOp(binaryOperator) ? | ||||||
| @ -523,14 +523,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| 	{ | 	{ | ||||||
| 		bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo; | 		bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo; | ||||||
| 		if (willBeWrittenTo) | 		if (willBeWrittenTo) | ||||||
| 			solAssert(!m_currentLValue, ""); | 			solAssert(!m_currentLValue); | ||||||
| 		if (_tuple.components().size() == 1) | 		if (_tuple.components().size() == 1) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(_tuple.components().front(), ""); | 			solAssert(_tuple.components().front()); | ||||||
| 			_tuple.components().front()->accept(*this); | 			_tuple.components().front()->accept(*this); | ||||||
| 			setLocation(_tuple); | 			setLocation(_tuple); | ||||||
| 			if (willBeWrittenTo) | 			if (willBeWrittenTo) | ||||||
| 				solAssert(!!m_currentLValue, ""); | 				solAssert(!!m_currentLValue); | ||||||
| 			else | 			else | ||||||
| 				define(_tuple, *_tuple.components().front()); | 				define(_tuple, *_tuple.components().front()); | ||||||
| 		} | 		} | ||||||
| @ -544,7 +544,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| 					setLocation(_tuple); | 					setLocation(_tuple); | ||||||
| 					if (willBeWrittenTo) | 					if (willBeWrittenTo) | ||||||
| 					{ | 					{ | ||||||
| 						solAssert(!!m_currentLValue, ""); | 						solAssert(!!m_currentLValue); | ||||||
| 						lvalues.emplace_back(std::move(m_currentLValue)); | 						lvalues.emplace_back(std::move(m_currentLValue)); | ||||||
| 						m_currentLValue.reset(); | 						m_currentLValue.reset(); | ||||||
| 					} | 					} | ||||||
| @ -568,7 +568,7 @@ bool IRGeneratorForStatements::visit(Block const& _block) | |||||||
| { | { | ||||||
| 	if (_block.unchecked()) | 	if (_block.unchecked()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(m_context.arithmetic() == Arithmetic::Checked, ""); | 		solAssert(m_context.arithmetic() == Arithmetic::Checked); | ||||||
| 		m_context.setArithmetic(Arithmetic::Wrapping); | 		m_context.setArithmetic(Arithmetic::Wrapping); | ||||||
| 	} | 	} | ||||||
| 	return true; | 	return true; | ||||||
| @ -578,7 +578,7 @@ void IRGeneratorForStatements::endVisit(Block const& _block) | |||||||
| { | { | ||||||
| 	if (_block.unchecked()) | 	if (_block.unchecked()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(m_context.arithmetic() == Arithmetic::Wrapping, ""); | 		solAssert(m_context.arithmetic() == Arithmetic::Wrapping); | ||||||
| 		m_context.setArithmetic(Arithmetic::Checked); | 		m_context.setArithmetic(Arithmetic::Checked); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -607,7 +607,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder) | void IRGeneratorForStatements::endVisit(PlaceholderStatement const& _placeholder) | ||||||
| { | { | ||||||
| 	solAssert(m_placeholderCallback, ""); | 	solAssert(m_placeholderCallback); | ||||||
| 	setLocation(_placeholder); | 	setLocation(_placeholder); | ||||||
| 	appendCode() << m_placeholderCallback(); | 	appendCode() << m_placeholderCallback(); | ||||||
| } | } | ||||||
| @ -776,7 +776,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | |||||||
| { | { | ||||||
| 	setLocation(_binOp); | 	setLocation(_binOp); | ||||||
| 
 | 
 | ||||||
| 	solAssert(!!_binOp.annotation().commonType, ""); | 	solAssert(!!_binOp.annotation().commonType); | ||||||
| 	Type const* commonType = _binOp.annotation().commonType; | 	Type const* commonType = _binOp.annotation().commonType; | ||||||
| 	langutil::Token op = _binOp.getOperator(); | 	langutil::Token op = _binOp.getOperator(); | ||||||
| 
 | 
 | ||||||
| @ -799,7 +799,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | |||||||
| 
 | 
 | ||||||
| 	if (TokenTraits::isCompareOp(op)) | 	if (TokenTraits::isCompareOp(op)) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(commonType->isValueType(), ""); | 		solAssert(commonType->isValueType()); | ||||||
| 
 | 
 | ||||||
| 		bool isSigned = false; | 		bool isSigned = false; | ||||||
| 		if (auto type = dynamic_cast<IntegerType const*>(commonType)) | 		if (auto type = dynamic_cast<IntegerType const*>(commonType)) | ||||||
| @ -855,7 +855,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | |||||||
| 		else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type)) | 		else if (auto rationalNumberType = dynamic_cast<RationalNumberType const*>(_binOp.leftExpression().annotation().type)) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation."); | 			solAssert(rationalNumberType->integerType(), "Invalid literal as the base for exponentiation."); | ||||||
| 			solAssert(dynamic_cast<IntegerType const*>(commonType), ""); | 			solAssert(dynamic_cast<IntegerType const*>(commonType)); | ||||||
| 
 | 
 | ||||||
| 			define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction( | 			define(_binOp) << m_utils.overflowCheckedIntLiteralExpFunction( | ||||||
| 				*rationalNumberType, | 				*rationalNumberType, | ||||||
| @ -951,7 +951,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	{ | 	{ | ||||||
| 		FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract()); | 		FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract()); | ||||||
| 
 | 
 | ||||||
| 		solAssert(!functionType->takesArbitraryParameters(), ""); | 		solAssert(!functionType->takesArbitraryParameters()); | ||||||
| 
 | 
 | ||||||
| 		vector<string> args; | 		vector<string> args; | ||||||
| 		if (functionType->bound()) | 		if (functionType->bound()) | ||||||
| @ -962,7 +962,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 
 | 
 | ||||||
| 		if (functionDef) | 		if (functionDef) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(functionDef->isImplemented(), ""); | 			solAssert(functionDef->isImplemented()); | ||||||
| 
 | 
 | ||||||
| 			define(_functionCall) << | 			define(_functionCall) << | ||||||
| 				m_context.enqueueFunctionForCodeGeneration(*functionDef) << | 				m_context.enqueueFunctionForCodeGeneration(*functionDef) << | ||||||
| @ -1065,7 +1065,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::Error: | 	case FunctionType::Kind::Error: | ||||||
| 	{ | 	{ | ||||||
| 		ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression())); | 		ErrorDefinition const* error = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(_functionCall.expression())); | ||||||
| 		solAssert(error, ""); | 		solAssert(error); | ||||||
| 		revertWithError( | 		revertWithError( | ||||||
| 			error->functionType(true)->externalSignature(), | 			error->functionType(true)->externalSignature(), | ||||||
| 			error->functionType(true)->parameterTypes(), | 			error->functionType(true)->parameterTypes(), | ||||||
| @ -1076,7 +1076,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::Wrap: | 	case FunctionType::Kind::Wrap: | ||||||
| 	case FunctionType::Kind::Unwrap: | 	case FunctionType::Kind::Unwrap: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(arguments.size() == 1, ""); | 		solAssert(arguments.size() == 1); | ||||||
| 		FunctionType::Kind kind = functionType->kind(); | 		FunctionType::Kind kind = functionType->kind(); | ||||||
| 		if (kind == FunctionType::Kind::Wrap) | 		if (kind == FunctionType::Kind::Wrap) | ||||||
| 			solAssert( | 			solAssert( | ||||||
| @ -1086,7 +1086,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 				"" | 				"" | ||||||
| 			); | 			); | ||||||
| 		else | 		else | ||||||
| 			solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType, ""); | 			solAssert(type(*arguments.at(0)).category() == Type::Category::UserDefinedValueType); | ||||||
| 
 | 
 | ||||||
| 		define(_functionCall, *arguments.at(0)); | 		define(_functionCall, *arguments.at(0)); | ||||||
| 		break; | 		break; | ||||||
| @ -1120,7 +1120,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::ABIEncodeWithSignature: | 	case FunctionType::Kind::ABIEncodeWithSignature: | ||||||
| 	{ | 	{ | ||||||
| 		bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked; | 		bool const isPacked = functionType->kind() == FunctionType::Kind::ABIEncodePacked; | ||||||
| 		solAssert(functionType->padArguments() != isPacked, ""); | 		solAssert(functionType->padArguments() != isPacked); | ||||||
| 		bool const hasSelectorOrSignature = | 		bool const hasSelectorOrSignature = | ||||||
| 			functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || | 			functionType->kind() == FunctionType::Kind::ABIEncodeWithSelector || | ||||||
| 			functionType->kind() == FunctionType::Kind::ABIEncodeCall || | 			functionType->kind() == FunctionType::Kind::ABIEncodeCall || | ||||||
| @ -1134,7 +1134,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 
 | 
 | ||||||
| 		if (functionType->kind() == FunctionType::Kind::ABIEncodeCall) | 		if (functionType->kind() == FunctionType::Kind::ABIEncodeCall) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(arguments.size() == 2, ""); | 			solAssert(arguments.size() == 2); | ||||||
| 			// Account for tuples with one component which become that component
 | 			// Account for tuples with one component which become that component
 | ||||||
| 			if (type(*arguments[1]).category() == Type::Category::Tuple) | 			if (type(*arguments[1]).category() == Type::Category::Tuple) | ||||||
| 			{ | 			{ | ||||||
| @ -1257,7 +1257,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 			referenceType && referenceType->dataStoredIn(DataLocation::CallData) | 			referenceType && referenceType->dataStoredIn(DataLocation::CallData) | ||||||
| 			) | 			) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), ""); | 			solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata())); | ||||||
| 			IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata()); | 			IRVariable var = convert(*arguments[0], *TypeProvider::bytesCalldata()); | ||||||
| 			templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false)); | 			templ("abiDecode", m_context.abiFunctions().tupleDecoder(targetTypes, false)); | ||||||
| 			templ("offset", var.part("offset").name()); | 			templ("offset", var.part("offset").name()); | ||||||
| @ -1279,8 +1279,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	} | 	} | ||||||
| 	case FunctionType::Kind::Revert: | 	case FunctionType::Kind::Revert: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(arguments.size() == parameterTypes.size(), ""); | 		solAssert(arguments.size() == parameterTypes.size()); | ||||||
| 		solAssert(arguments.size() <= 1, ""); | 		solAssert(arguments.size() <= 1); | ||||||
| 		solAssert( | 		solAssert( | ||||||
| 			arguments.empty() || | 			arguments.empty() || | ||||||
| 			arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), | 			arguments.front()->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), | ||||||
| @ -1299,7 +1299,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::ObjectCreation: | 	case FunctionType::Kind::ObjectCreation: | ||||||
| 	{ | 	{ | ||||||
| 		ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); | 		ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); | ||||||
| 		solAssert(arguments.size() == 1, ""); | 		solAssert(arguments.size() == 1); | ||||||
| 
 | 
 | ||||||
| 		IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); | 		IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); | ||||||
| 		define(_functionCall) << | 		define(_functionCall) << | ||||||
| @ -1311,7 +1311,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	} | 	} | ||||||
| 	case FunctionType::Kind::KECCAK256: | 	case FunctionType::Kind::KECCAK256: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(arguments.size() == 1, ""); | 		solAssert(arguments.size() == 1); | ||||||
| 
 | 
 | ||||||
| 		ArrayType const* arrayType = TypeProvider::bytesMemory(); | 		ArrayType const* arrayType = TypeProvider::bytesMemory(); | ||||||
| 
 | 
 | ||||||
| @ -1339,10 +1339,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	} | 	} | ||||||
| 	case FunctionType::Kind::ArrayPop: | 	case FunctionType::Kind::ArrayPop: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(functionType->bound(), ""); | 		solAssert(functionType->bound()); | ||||||
| 		solAssert(functionType->parameterTypes().empty(), ""); | 		solAssert(functionType->parameterTypes().empty()); | ||||||
| 		ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); | 		ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); | ||||||
| 		solAssert(arrayType, ""); | 		solAssert(arrayType); | ||||||
| 		define(_functionCall) << | 		define(_functionCall) << | ||||||
| 			m_utils.storageArrayPopFunction(*arrayType) << | 			m_utils.storageArrayPopFunction(*arrayType) << | ||||||
| 			"(" << | 			"(" << | ||||||
| @ -1353,7 +1353,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::ArrayPush: | 	case FunctionType::Kind::ArrayPush: | ||||||
| 	{ | 	{ | ||||||
| 		ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); | 		ArrayType const* arrayType = dynamic_cast<ArrayType const*>(functionType->selfType()); | ||||||
| 		solAssert(arrayType, ""); | 		solAssert(arrayType); | ||||||
| 
 | 
 | ||||||
| 		if (arguments.empty()) | 		if (arguments.empty()) | ||||||
| 		{ | 		{ | ||||||
| @ -1414,8 +1414,8 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 			{FunctionType::Kind::AddMod, "addmod"}, | 			{FunctionType::Kind::AddMod, "addmod"}, | ||||||
| 			{FunctionType::Kind::MulMod, "mulmod"}, | 			{FunctionType::Kind::MulMod, "mulmod"}, | ||||||
| 		}; | 		}; | ||||||
| 		solAssert(functions.find(functionType->kind()) != functions.end(), ""); | 		solAssert(functions.find(functionType->kind()) != functions.end()); | ||||||
| 		solAssert(arguments.size() == 3 && parameterTypes.size() == 3, ""); | 		solAssert(arguments.size() == 3 && parameterTypes.size() == 3); | ||||||
| 
 | 
 | ||||||
| 		IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2])); | 		IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2])); | ||||||
| 		define(modulus, *arguments[2]); | 		define(modulus, *arguments[2]); | ||||||
| @ -1440,7 +1440,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 			{FunctionType::Kind::Selfdestruct, "selfdestruct"}, | 			{FunctionType::Kind::Selfdestruct, "selfdestruct"}, | ||||||
| 			{FunctionType::Kind::BlockHash, "blockhash"}, | 			{FunctionType::Kind::BlockHash, "blockhash"}, | ||||||
| 		}; | 		}; | ||||||
| 		solAssert(functions.find(functionType->kind()) != functions.end(), ""); | 		solAssert(functions.find(functionType->kind()) != functions.end()); | ||||||
| 
 | 
 | ||||||
| 		string args; | 		string args; | ||||||
| 		for (size_t i = 0; i < arguments.size(); ++i) | 		for (size_t i = 0; i < arguments.size(); ++i) | ||||||
| @ -1497,7 +1497,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 		t("saltSet", functionType->saltSet()); | 		t("saltSet", functionType->saltSet()); | ||||||
| 		if (functionType->saltSet()) | 		if (functionType->saltSet()) | ||||||
| 			t("salt", IRVariable(_functionCall.expression()).part("salt").name()); | 			t("salt", IRVariable(_functionCall.expression()).part("salt").name()); | ||||||
| 		solAssert(IRVariable(_functionCall).stackSlots().size() == 1, ""); | 		solAssert(IRVariable(_functionCall).stackSlots().size() == 1); | ||||||
| 		t("address", IRVariable(_functionCall).commaSeparatedList()); | 		t("address", IRVariable(_functionCall).commaSeparatedList()); | ||||||
| 		t("isTryCall", _functionCall.annotation().tryCall); | 		t("isTryCall", _functionCall.annotation().tryCall); | ||||||
| 		if (_functionCall.annotation().tryCall) | 		if (_functionCall.annotation().tryCall) | ||||||
| @ -1511,7 +1511,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::Send: | 	case FunctionType::Kind::Send: | ||||||
| 	case FunctionType::Kind::Transfer: | 	case FunctionType::Kind::Transfer: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(arguments.size() == 1 && parameterTypes.size() == 1, ""); | 		solAssert(arguments.size() == 1 && parameterTypes.size() == 1); | ||||||
| 		string address{IRVariable(_functionCall.expression()).part("address").name()}; | 		string address{IRVariable(_functionCall.expression()).part("address").name()}; | ||||||
| 		string value{expressionAsType(*arguments[0], *(parameterTypes[0]))}; | 		string value{expressionAsType(*arguments[0], *(parameterTypes[0]))}; | ||||||
| 		Whiskers templ(R"( | 		Whiskers templ(R"( | ||||||
| @ -1540,10 +1540,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 	case FunctionType::Kind::RIPEMD160: | 	case FunctionType::Kind::RIPEMD160: | ||||||
| 	case FunctionType::Kind::SHA256: | 	case FunctionType::Kind::SHA256: | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(!_functionCall.annotation().tryCall, ""); | 		solAssert(!_functionCall.annotation().tryCall); | ||||||
| 		solAssert(!functionType->valueSet(), ""); | 		solAssert(!functionType->valueSet()); | ||||||
| 		solAssert(!functionType->gasSet(), ""); | 		solAssert(!functionType->gasSet()); | ||||||
| 		solAssert(!functionType->bound(), ""); | 		solAssert(!functionType->bound()); | ||||||
| 
 | 
 | ||||||
| 		static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = { | 		static map<FunctionType::Kind, std::tuple<unsigned, size_t>> precompiles = { | ||||||
| 			{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)}, | 			{FunctionType::Kind::ECRecover, std::make_tuple(1, 0)}, | ||||||
| @ -1618,7 +1618,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | |||||||
| 	for (size_t i = 0; i < _options.names().size(); ++i) | 	for (size_t i = 0; i < _options.names().size(); ++i) | ||||||
| 	{ | 	{ | ||||||
| 		string const& name = *_options.names()[i]; | 		string const& name = *_options.names()[i]; | ||||||
| 		solAssert(name == "salt" || name == "gas" || name == "value", ""); | 		solAssert(name == "salt" || name == "gas" || name == "value"); | ||||||
| 
 | 
 | ||||||
| 		define(IRVariable(_options).part(name), *_options.options()[i]); | 		define(IRVariable(_options).part(name), *_options.options()[i]); | ||||||
| 	} | 	} | ||||||
| @ -1636,7 +1636,7 @@ bool IRGeneratorForStatements::visit(MemberAccess const& _memberAccess) | |||||||
| 		innerExpression->expression().annotation().type->category() == Type::Category::Address | 		innerExpression->expression().annotation().type->category() == Type::Category::Address | ||||||
| 	) | 	) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(innerExpression->annotation().type->category() == Type::Category::Array, ""); | 		solAssert(innerExpression->annotation().type->category() == Type::Category::Array); | ||||||
| 		// Skip visiting <address>.code
 | 		// Skip visiting <address>.code
 | ||||||
| 		innerExpression->expression().accept(*this); | 		innerExpression->expression().accept(*this); | ||||||
| 
 | 
 | ||||||
| @ -1657,7 +1657,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 	if (memberFunctionType && memberFunctionType->bound()) | 	if (memberFunctionType && memberFunctionType->bound()) | ||||||
| 	{ | 	{ | ||||||
| 		define(IRVariable(_memberAccess).part("self"), _memberAccess.expression()); | 		define(IRVariable(_memberAccess).part("self"), _memberAccess.expression()); | ||||||
| 		solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); | 		solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static); | ||||||
| 		if (memberFunctionType->kind() == FunctionType::Kind::Internal) | 		if (memberFunctionType->kind() == FunctionType::Kind::Internal) | ||||||
| 			assignInternalFunctionIDIfNotCalledDirectly( | 			assignInternalFunctionIDIfNotCalledDirectly( | ||||||
| 				_memberAccess, | 				_memberAccess, | ||||||
| @ -1673,9 +1673,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 		else | 		else | ||||||
| 		{ | 		{ | ||||||
| 			auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration()); | 			auto const& functionDefinition = dynamic_cast<FunctionDefinition const&>(memberFunctionType->declaration()); | ||||||
| 			solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall, ""); | 			solAssert(memberFunctionType->kind() == FunctionType::Kind::DelegateCall); | ||||||
| 			auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope()); | 			auto contract = dynamic_cast<ContractDefinition const*>(functionDefinition.scope()); | ||||||
| 			solAssert(contract && contract->isLibrary(), ""); | 			solAssert(contract && contract->isLibrary()); | ||||||
| 			define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; | 			define(IRVariable(_memberAccess).part("address")) << linkerSymbol(*contract) << "\n"; | ||||||
| 			define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n"; | 			define(IRVariable(_memberAccess).part("functionSelector")) << memberFunctionType->externalIdentifier() << "\n"; | ||||||
| 		} | 		} | ||||||
| @ -1688,7 +1688,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 	{ | 	{ | ||||||
| 		ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); | 		ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); | ||||||
| 		if (type.isSuper()) | 		if (type.isSuper()) | ||||||
| 			solAssert(false, ""); | 			solAssert(false); | ||||||
| 
 | 
 | ||||||
| 		// ordinary contract type
 | 		// ordinary contract type
 | ||||||
| 		else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) | 		else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) | ||||||
| @ -1736,7 +1736,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 				")\n"; | 				")\n"; | ||||||
| 		else if (set<string>{"send", "transfer"}.count(member)) | 		else if (set<string>{"send", "transfer"}.count(member)) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); | 			solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable); | ||||||
| 			define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); | 			define(IRVariable{_memberAccess}.part("address"), _memberAccess.expression()); | ||||||
| 		} | 		} | ||||||
| 		else if (set<string>{"call", "callcode", "delegatecall", "staticcall"}.count(member)) | 		else if (set<string>{"call", "callcode", "delegatecall", "staticcall"}.count(member)) | ||||||
| @ -1764,7 +1764,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 				functionType.kind() == FunctionType::Kind::Internal | 				functionType.kind() == FunctionType::Kind::Internal | ||||||
| 			) | 			) | ||||||
| 			{ | 			{ | ||||||
| 				solAssert(functionType.hasDeclaration(), ""); | 				solAssert(functionType.hasDeclaration()); | ||||||
| 				solAssert( | 				solAssert( | ||||||
| 					functionType.kind() == FunctionType::Kind::Error || | 					functionType.kind() == FunctionType::Kind::Error || | ||||||
| 					functionType.declaration().isPartOfExternalInterface(), | 					functionType.declaration().isPartOfExternalInterface(), | ||||||
| @ -1832,7 +1832,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 		{ | 		{ | ||||||
| 			Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); | 			Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); | ||||||
| 			auto const& contractType = dynamic_cast<ContractType const&>(*arg); | 			auto const& contractType = dynamic_cast<ContractType const&>(*arg); | ||||||
| 			solAssert(!contractType.isSuper(), ""); | 			solAssert(!contractType.isSuper()); | ||||||
| 			ContractDefinition const& contract = contractType.contractDefinition(); | 			ContractDefinition const& contract = contractType.contractDefinition(); | ||||||
| 			m_context.subObjectsCreated().insert(&contract); | 			m_context.subObjectsCreated().insert(&contract); | ||||||
| 			appendCode() << Whiskers(R"( | 			appendCode() << Whiskers(R"( | ||||||
| @ -1856,7 +1856,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 		{ | 		{ | ||||||
| 			Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); | 			Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); | ||||||
| 			auto const& contractType = dynamic_cast<ContractType const&>(*arg); | 			auto const& contractType = dynamic_cast<ContractType const&>(*arg); | ||||||
| 			solAssert(!contractType.isSuper(), ""); | 			solAssert(!contractType.isSuper()); | ||||||
| 			ContractDefinition const& contract = contractType.contractDefinition(); | 			ContractDefinition const& contract = contractType.contractDefinition(); | ||||||
| 			define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; | 			define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; | ||||||
| 		} | 		} | ||||||
| @ -1983,7 +1983,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 		} | 		} | ||||||
| 		else if (member == "pop" || member == "push") | 		else if (member == "pop" || member == "push") | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(type.location() == DataLocation::Storage, ""); | 			solAssert(type.location() == DataLocation::Storage); | ||||||
| 			define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot")); | 			define(IRVariable{_memberAccess}.part("slot"), IRVariable{_memberAccess.expression()}.part("slot")); | ||||||
| 		} | 		} | ||||||
| 		else | 		else | ||||||
| @ -2019,8 +2019,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 						*_memberAccess.annotation().referencedDeclaration | 						*_memberAccess.annotation().referencedDeclaration | ||||||
| 					).resolveVirtual(m_context.mostDerivedContract(), super); | 					).resolveVirtual(m_context.mostDerivedContract(), super); | ||||||
| 
 | 
 | ||||||
| 				solAssert(resolvedFunctionDef.functionType(true), ""); | 				solAssert(resolvedFunctionDef.functionType(true)); | ||||||
| 				solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); | 				solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal); | ||||||
| 				assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef); | 				assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, resolvedFunctionDef); | ||||||
| 			} | 			} | ||||||
| 			else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) | 			else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) | ||||||
| @ -2078,19 +2078,19 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 				// The old code generator had a generic "else" case here
 | 				// The old code generator had a generic "else" case here
 | ||||||
| 				// without any specific code being generated,
 | 				// without any specific code being generated,
 | ||||||
| 				// but it would still be better to have an exhaustive list.
 | 				// but it would still be better to have an exhaustive list.
 | ||||||
| 				solAssert(false, ""); | 				solAssert(false); | ||||||
| 		} | 		} | ||||||
| 		else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType)) | 		else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType)) | ||||||
| 			define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n"; | 			define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n"; | ||||||
| 		else if (dynamic_cast<UserDefinedValueType const*>(&actualType)) | 		else if (dynamic_cast<UserDefinedValueType const*>(&actualType)) | ||||||
| 			solAssert(member == "wrap" || member == "unwrap", ""); | 			solAssert(member == "wrap" || member == "unwrap"); | ||||||
| 		else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType)) | 		else if (auto const* arrayType = dynamic_cast<ArrayType const*>(&actualType)) | ||||||
| 			solAssert(arrayType->isByteArray() && member == "concat", ""); | 			solAssert(arrayType->isByteArray() && member == "concat"); | ||||||
| 		else | 		else | ||||||
| 			// The old code generator had a generic "else" case here
 | 			// The old code generator had a generic "else" case here
 | ||||||
| 			// without any specific code being generated,
 | 			// without any specific code being generated,
 | ||||||
| 			// but it would still be better to have an exhaustive list.
 | 			// but it would still be better to have an exhaustive list.
 | ||||||
| 			solAssert(false, ""); | 			solAssert(false); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	case Type::Category::Module: | 	case Type::Category::Module: | ||||||
| @ -2106,17 +2106,17 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 		); | 		); | ||||||
| 		if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) | 		if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(variable->isConstant(), ""); | 			solAssert(variable->isConstant()); | ||||||
| 			handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess)); | 			handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess)); | ||||||
| 		} | 		} | ||||||
| 		else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) | 		else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) | ||||||
| 		{ | 		{ | ||||||
| 			auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); | 			auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); | ||||||
| 			solAssert(function && function->isFree(), ""); | 			solAssert(function && function->isFree()); | ||||||
| 			solAssert(function->functionType(true), ""); | 			solAssert(function->functionType(true)); | ||||||
| 			solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, ""); | 			solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal); | ||||||
| 			solAssert(funType->kind() == FunctionType::Kind::Internal, ""); | 			solAssert(funType->kind() == FunctionType::Kind::Internal); | ||||||
| 			solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); | 			solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static); | ||||||
| 
 | 
 | ||||||
| 			assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function); | 			assignInternalFunctionIDIfNotCalledDirectly(_memberAccess, *function); | ||||||
| 		} | 		} | ||||||
| @ -2140,7 +2140,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | |||||||
| 
 | 
 | ||||||
| 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | ||||||
| 
 | 
 | ||||||
| 	solAssert(holds_alternative<yul::Block>(modified), ""); | 	solAssert(holds_alternative<yul::Block>(modified)); | ||||||
| 
 | 
 | ||||||
| 	// Do not provide dialect so that we get the full type information.
 | 	// Do not provide dialect so that we get the full type information.
 | ||||||
| 	appendCode() << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n"; | 	appendCode() << yul::AsmPrinter()(std::get<yul::Block>(modified)) << "\n"; | ||||||
| @ -2183,7 +2183,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | |||||||
| 			dynamic_cast<ArraySliceType const&>(baseType).arrayType(); | 			dynamic_cast<ArraySliceType const&>(baseType).arrayType(); | ||||||
| 
 | 
 | ||||||
| 		if (baseType.category() == Type::Category::ArraySlice) | 		if (baseType.category() == Type::Category::ArraySlice) | ||||||
| 			solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized(), ""); | 			solAssert(arrayType.dataStoredIn(DataLocation::CallData) && arrayType.isDynamicallySized()); | ||||||
| 
 | 
 | ||||||
| 		solAssert(_indexAccess.indexExpression(), "Index expression expected."); | 		solAssert(_indexAccess.indexExpression(), "Index expression expected."); | ||||||
| 
 | 
 | ||||||
| @ -2276,8 +2276,8 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | |||||||
| 	} | 	} | ||||||
| 	else if (baseType.category() == Type::Category::TypeType) | 	else if (baseType.category() == Type::Category::TypeType) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(baseType.sizeOnStack() == 0, ""); | 		solAssert(baseType.sizeOnStack() == 0); | ||||||
| 		solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, ""); | 		solAssert(_indexAccess.annotation().type->sizeOnStack() == 0); | ||||||
| 		// no-op - this seems to be a lone array type (`structType[];`)
 | 		// no-op - this seems to be a lone array type (`structType[];`)
 | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| @ -2302,7 +2302,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces | |||||||
| 	{ | 	{ | ||||||
| 		case DataLocation::CallData: | 		case DataLocation::CallData: | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(baseType.isDynamicallySized(), ""); | 			solAssert(baseType.isDynamicallySized()); | ||||||
| 			IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()}; | 			IRVariable sliceStart{m_context.newYulVariable(), *TypeProvider::uint256()}; | ||||||
| 			if (_indexRangeAccess.startExpression()) | 			if (_indexRangeAccess.startExpression()) | ||||||
| 				define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()}); | 				define(sliceStart, IRVariable{*_indexRangeAccess.startExpression()}); | ||||||
| @ -2340,18 +2340,18 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) | |||||||
| 		switch (magicVar->type()->category()) | 		switch (magicVar->type()->category()) | ||||||
| 		{ | 		{ | ||||||
| 		case Type::Category::Contract: | 		case Type::Category::Contract: | ||||||
| 			solAssert(_identifier.name() == "this", ""); | 			solAssert(_identifier.name() == "this"); | ||||||
| 			define(_identifier) << "address()\n"; | 			define(_identifier) << "address()\n"; | ||||||
| 			break; | 			break; | ||||||
| 		case Type::Category::Integer: | 		case Type::Category::Integer: | ||||||
| 			solAssert(_identifier.name() == "now", ""); | 			solAssert(_identifier.name() == "now"); | ||||||
| 			define(_identifier) << "timestamp()\n"; | 			define(_identifier) << "timestamp()\n"; | ||||||
| 			break; | 			break; | ||||||
| 		case Type::Category::TypeType: | 		case Type::Category::TypeType: | ||||||
| 		{ | 		{ | ||||||
| 			auto typeType = dynamic_cast<TypeType const*>(magicVar->type()); | 			auto typeType = dynamic_cast<TypeType const*>(magicVar->type()); | ||||||
| 			if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType())) | 			if (auto contractType = dynamic_cast<ContractType const*>(typeType->actualType())) | ||||||
| 				solAssert(!contractType->isSuper() || _identifier.name() == "super", ""); | 				solAssert(!contractType->isSuper() || _identifier.name() == "super"); | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
| 		default: | 		default: | ||||||
| @ -2361,11 +2361,11 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) | |||||||
| 	} | 	} | ||||||
| 	else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) | 	else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, ""); | 		solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual); | ||||||
| 		FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract()); | 		FunctionDefinition const& resolvedFunctionDef = functionDef->resolveVirtual(m_context.mostDerivedContract()); | ||||||
| 
 | 
 | ||||||
| 		solAssert(resolvedFunctionDef.functionType(true), ""); | 		solAssert(resolvedFunctionDef.functionType(true)); | ||||||
| 		solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); | 		solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal); | ||||||
| 		assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef); | 		assignInternalFunctionIDIfNotCalledDirectly(_identifier, resolvedFunctionDef); | ||||||
| 	} | 	} | ||||||
| 	else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) | 	else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) | ||||||
| @ -2460,9 +2460,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | |||||||
| ) | ) | ||||||
| { | { | ||||||
| 	FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression())); | 	FunctionType const& funType = dynamic_cast<FunctionType const&>(type(_functionCall.expression())); | ||||||
| 	solAssert(!funType.takesArbitraryParameters(), ""); | 	solAssert(!funType.takesArbitraryParameters()); | ||||||
| 	solAssert(_arguments.size() == funType.parameterTypes().size(), ""); | 	solAssert(_arguments.size() == funType.parameterTypes().size()); | ||||||
| 	solAssert(!funType.isBareCall(), ""); | 	solAssert(!funType.isBareCall()); | ||||||
| 	FunctionType::Kind const funKind = funType.kind(); | 	FunctionType::Kind const funKind = funType.kind(); | ||||||
| 
 | 
 | ||||||
| 	solAssert( | 	solAssert( | ||||||
| @ -2566,7 +2566,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | |||||||
| 
 | 
 | ||||||
| 	string const retVars = IRVariable(_functionCall).commaSeparatedList(); | 	string const retVars = IRVariable(_functionCall).commaSeparatedList(); | ||||||
| 	templ("retVars", retVars); | 	templ("retVars", retVars); | ||||||
| 	solAssert(retVars.empty() == returnInfo.returnTypes.empty(), ""); | 	solAssert(retVars.empty() == returnInfo.returnTypes.empty()); | ||||||
| 
 | 
 | ||||||
| 	templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true)); | 	templ("abiDecode", m_context.abiFunctions().tupleDecoder(returnInfo.returnTypes, true)); | ||||||
| 	templ("dynamicReturnSize", returnInfo.dynamicReturnSize); | 	templ("dynamicReturnSize", returnInfo.dynamicReturnSize); | ||||||
| @ -2575,7 +2575,7 @@ void IRGeneratorForStatements::appendExternalFunctionCall( | |||||||
| 
 | 
 | ||||||
| 	bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; | 	bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; | ||||||
| 
 | 
 | ||||||
| 	solAssert(funType.padArguments(), ""); | 	solAssert(funType.padArguments()); | ||||||
| 	templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall)); | 	templ("encodeArgs", m_context.abiFunctions().tupleEncoder(argumentTypes, parameterTypes, encodeForLibraryCall)); | ||||||
| 	templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); | 	templ("argumentString", joinHumanReadablePrefixed(argumentStrings)); | ||||||
| 
 | 
 | ||||||
| @ -2628,7 +2628,7 @@ void IRGeneratorForStatements::appendBareCall( | |||||||
| 	); | 	); | ||||||
| 	FunctionType::Kind const funKind = funType.kind(); | 	FunctionType::Kind const funKind = funType.kind(); | ||||||
| 
 | 
 | ||||||
| 	solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); | 	solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall()); | ||||||
| 	solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); | 	solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); | ||||||
| 	solAssert( | 	solAssert( | ||||||
| 		funKind == FunctionType::Kind::BareCall || | 		funKind == FunctionType::Kind::BareCall || | ||||||
| @ -2636,7 +2636,7 @@ void IRGeneratorForStatements::appendBareCall( | |||||||
| 		funKind == FunctionType::Kind::BareStaticCall, "" | 		funKind == FunctionType::Kind::BareStaticCall, "" | ||||||
| 	); | 	); | ||||||
| 
 | 
 | ||||||
| 	solAssert(!_functionCall.annotation().tryCall, ""); | 	solAssert(!_functionCall.annotation().tryCall); | ||||||
| 	Whiskers templ(R"( | 	Whiskers templ(R"( | ||||||
| 		<?needsEncoding> | 		<?needsEncoding> | ||||||
| 			let <pos> := <allocateUnbounded>() | 			let <pos> := <allocateUnbounded>() | ||||||
| @ -2848,7 +2848,7 @@ string IRGeneratorForStatements::binaryOperation( | |||||||
| 			"Not yet implemented - FixedPointType." | 			"Not yet implemented - FixedPointType." | ||||||
| 		); | 		); | ||||||
| 		IntegerType const* type = dynamic_cast<IntegerType const*>(&_type); | 		IntegerType const* type = dynamic_cast<IntegerType const*>(&_type); | ||||||
| 		solAssert(type, ""); | 		solAssert(type); | ||||||
| 		bool checked = m_context.arithmetic() == Arithmetic::Checked; | 		bool checked = m_context.arithmetic() == Arithmetic::Checked; | ||||||
| 		switch (_operator) | 		switch (_operator) | ||||||
| 		{ | 		{ | ||||||
| @ -2888,9 +2888,9 @@ std::string IRGeneratorForStatements::shiftOperation( | |||||||
| 		"Not yet implemented - FixedPointType." | 		"Not yet implemented - FixedPointType." | ||||||
| 	); | 	); | ||||||
| 	IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type()); | 	IntegerType const* amountType = dynamic_cast<IntegerType const*>(&_amountToShift.type()); | ||||||
| 	solAssert(amountType, ""); | 	solAssert(amountType); | ||||||
| 
 | 
 | ||||||
| 	solAssert(_operator == Token::SHL || _operator == Token::SAR, ""); | 	solAssert(_operator == Token::SHL || _operator == Token::SAR); | ||||||
| 
 | 
 | ||||||
| 	return | 	return | ||||||
| 		Whiskers(R"( | 		Whiskers(R"( | ||||||
| @ -2909,7 +2909,7 @@ std::string IRGeneratorForStatements::shiftOperation( | |||||||
| void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp) | void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _binOp) | ||||||
| { | { | ||||||
| 	langutil::Token const op = _binOp.getOperator(); | 	langutil::Token const op = _binOp.getOperator(); | ||||||
| 	solAssert(op == Token::Or || op == Token::And, ""); | 	solAssert(op == Token::Or || op == Token::And); | ||||||
| 
 | 
 | ||||||
| 	_binOp.leftExpression().accept(*this); | 	_binOp.leftExpression().accept(*this); | ||||||
| 	setLocation(_binOp); | 	setLocation(_binOp); | ||||||
| @ -2956,7 +2956,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable | |||||||
| 
 | 
 | ||||||
| 					if (_memory.byteArrayElement) | 					if (_memory.byteArrayElement) | ||||||
| 					{ | 					{ | ||||||
| 						solAssert(_lvalue.type == *TypeProvider::byte(), ""); | 						solAssert(_lvalue.type == *TypeProvider::byte()); | ||||||
| 						appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n"; | 						appendCode() << "mstore8(" + _memory.address + ", byte(0, " + prepared.commaSeparatedList() + "))\n"; | ||||||
| 					} | 					} | ||||||
| 					else | 					else | ||||||
| @ -2980,9 +2980,9 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable | |||||||
| 				} | 				} | ||||||
| 				else | 				else | ||||||
| 				{ | 				{ | ||||||
| 					solAssert(_lvalue.type.sizeOnStack() == 1, ""); | 					solAssert(_lvalue.type.sizeOnStack() == 1); | ||||||
| 					auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type()); | 					auto const* valueReferenceType = dynamic_cast<ReferenceType const*>(&_value.type()); | ||||||
| 					solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory), ""); | 					solAssert(valueReferenceType && valueReferenceType->dataStoredIn(DataLocation::Memory)); | ||||||
| 					appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; | 					appendCode() << "mstore(" + _memory.address + ", " + _value.part("mpos").name() + ")\n"; | ||||||
| 				} | 				} | ||||||
| 			}, | 			}, | ||||||
| @ -2991,7 +2991,7 @@ void IRGeneratorForStatements::writeToLValue(IRLValue const& _lvalue, IRVariable | |||||||
| 			{ | 			{ | ||||||
| 				solUnimplementedAssert(_lvalue.type.isValueType()); | 				solUnimplementedAssert(_lvalue.type.isValueType()); | ||||||
| 				solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); | 				solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); | ||||||
| 				solAssert(_lvalue.type == *_immutable.variable->type(), ""); | 				solAssert(_lvalue.type == *_immutable.variable->type()); | ||||||
| 				size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable); | 				size_t memOffset = m_context.immutableMemoryOffset(*_immutable.variable); | ||||||
| 
 | 
 | ||||||
| 				IRVariable prepared(m_context.newYulVariable(), _lvalue.type); | 				IRVariable prepared(m_context.newYulVariable(), _lvalue.type); | ||||||
| @ -3051,7 +3051,7 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) | |||||||
| 		[&](IRLValue::Immutable const& _immutable) { | 		[&](IRLValue::Immutable const& _immutable) { | ||||||
| 			solUnimplementedAssert(_lvalue.type.isValueType()); | 			solUnimplementedAssert(_lvalue.type.isValueType()); | ||||||
| 			solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); | 			solUnimplementedAssert(_lvalue.type.sizeOnStack() == 1); | ||||||
| 			solAssert(_lvalue.type == *_immutable.variable->type(), ""); | 			solAssert(_lvalue.type == *_immutable.variable->type()); | ||||||
| 			if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation) | 			if (m_context.executionContext() == IRGenerationContext::ExecutionContext::Creation) | ||||||
| 			{ | 			{ | ||||||
| 				string readFunction = m_utils.readFromMemory(*_immutable.variable->type()); | 				string readFunction = m_utils.readFromMemory(*_immutable.variable->type()); | ||||||
| @ -3073,13 +3073,13 @@ IRVariable IRGeneratorForStatements::readFromLValue(IRLValue const& _lvalue) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue) | void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue _lvalue) | ||||||
| { | { | ||||||
| 	solAssert(!m_currentLValue, ""); | 	solAssert(!m_currentLValue); | ||||||
| 
 | 
 | ||||||
| 	if (_expression.annotation().willBeWrittenTo) | 	if (_expression.annotation().willBeWrittenTo) | ||||||
| 	{ | 	{ | ||||||
| 		m_currentLValue.emplace(std::move(_lvalue)); | 		m_currentLValue.emplace(std::move(_lvalue)); | ||||||
| 		if (_lvalue.type.dataStoredIn(DataLocation::CallData)) | 		if (_lvalue.type.dataStoredIn(DataLocation::CallData)) | ||||||
| 			solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind), ""); | 			solAssert(holds_alternative<IRLValue::Stack>(_lvalue.kind)); | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		// Only define the expression, if it will not be written to.
 | 		// Only define the expression, if it will not be written to.
 | ||||||
| @ -3153,7 +3153,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) | |||||||
| 		size_t i = 0; | 		size_t i = 0; | ||||||
| 		for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters()) | 		for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters()) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(varDecl, ""); | 			solAssert(varDecl); | ||||||
| 			define(m_context.addLocalVariable(*varDecl), | 			define(m_context.addLocalVariable(*varDecl), | ||||||
| 				successClause.parameters()->parameters().size() == 1 ? | 				successClause.parameters()->parameters().size() == 1 ? | ||||||
| 				IRVariable(externalCall) : | 				IRVariable(externalCall) : | ||||||
| @ -3194,7 +3194,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement) | |||||||
| 		appendCode() << runFallback << " := 0\n"; | 		appendCode() << runFallback << " := 0\n"; | ||||||
| 		if (errorClause->parameters()) | 		if (errorClause->parameters()) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(errorClause->parameters()->parameters().size() == 1, ""); | 			solAssert(errorClause->parameters()->parameters().size() == 1); | ||||||
| 			IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front()); | 			IRVariable const& var = m_context.addLocalVariable(*errorClause->parameters()->parameters().front()); | ||||||
| 			define(var) << dataVariable << "\n"; | 			define(var) << dataVariable << "\n"; | ||||||
| 		} | 		} | ||||||
| @ -3215,7 +3215,7 @@ void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement) | |||||||
| 		appendCode() << runFallback << " := 0\n"; | 		appendCode() << runFallback << " := 0\n"; | ||||||
| 		if (panicClause->parameters()) | 		if (panicClause->parameters()) | ||||||
| 		{ | 		{ | ||||||
| 			solAssert(panicClause->parameters()->parameters().size() == 1, ""); | 			solAssert(panicClause->parameters()->parameters().size() == 1); | ||||||
| 			IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front()); | 			IRVariable const& var = m_context.addLocalVariable(*panicClause->parameters()->parameters().front()); | ||||||
| 			define(var) << code << "\n"; | 			define(var) << code << "\n"; | ||||||
| 		} | 		} | ||||||
| @ -3241,7 +3241,7 @@ void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallba | |||||||
| 	setLocation(_fallback); | 	setLocation(_fallback); | ||||||
| 	if (_fallback.parameters()) | 	if (_fallback.parameters()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(m_context.evmVersion().supportsReturndata(), ""); | 		solAssert(m_context.evmVersion().supportsReturndata()); | ||||||
| 		solAssert( | 		solAssert( | ||||||
| 			_fallback.parameters()->parameters().size() == 1 && | 			_fallback.parameters()->parameters().size() == 1 && | ||||||
| 			_fallback.parameters()->parameters().front() && | 			_fallback.parameters()->parameters().front() && | ||||||
| @ -3277,7 +3277,7 @@ void IRGeneratorForStatements::revertWithError( | |||||||
| 	for (ASTPointer<Expression const> const& arg: _errorArguments) | 	for (ASTPointer<Expression const> const& arg: _errorArguments) | ||||||
| 	{ | 	{ | ||||||
| 		errorArgumentVars += IRVariable(*arg).stackSlots(); | 		errorArgumentVars += IRVariable(*arg).stackSlots(); | ||||||
| 		solAssert(arg->annotation().type, ""); | 		solAssert(arg->annotation().type); | ||||||
| 		errorArgumentTypes.push_back(arg->annotation().type); | 		errorArgumentTypes.push_back(arg->annotation().type); | ||||||
| 	} | 	} | ||||||
| 	templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars)); | 	templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars)); | ||||||
| @ -3295,6 +3295,6 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) | |||||||
| 
 | 
 | ||||||
| string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const | string IRGeneratorForStatements::linkerSymbol(ContractDefinition const& _library) const | ||||||
| { | { | ||||||
| 	solAssert(_library.isLibrary(), ""); | 	solAssert(_library.isLibrary()); | ||||||
| 	return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; | 	return "linkersymbol(" + util::escapeAndQuoteString(_library.fullyQualifiedName()) + ")"; | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,11 @@ | |||||||
|  | // Triggered ICE before | ||||||
|  | contract C { | ||||||
|  | 	function f(string calldata data) external pure returns(string memory) { | ||||||
|  | 		bytes calldata test = bytes(data[:3]); | ||||||
|  | 		return string(test); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // f(string): 0x20, 3, "123" -> 0x20, 3, "123" | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user