Merge pull request #9208 from ethereum/develop

Merge develop into breaking.
This commit is contained in:
chriseth 2020-06-16 16:11:41 +02:00 committed by GitHub
commit 71cb7551f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 822 additions and 411 deletions

View File

@ -28,11 +28,11 @@ Language Features:
Compiler Features:
* NatSpec: Add fields "kind" and "version" to the JSON output.
* Commandline Interface: Prevent some incompatible commandline options from being used together.
Bugfixes:
* NatSpec: Do not consider ``////`` and ``/***`` as NatSpec comments.
### 0.6.10 (2020-06-11)

View File

@ -314,6 +314,16 @@ you should fork Solidity and add your personal fork as a second remote:
git remote add personal git@github.com:[username]/solidity.git
.. note::
This method will result in a prerelease build leading to e.g. a flag
being set in each bytecode produced by such a compiler.
If you want to re-build a released Solidity compiler, then
please use the source tarball on the github release page:
https://github.com/ethereum/solidity/releases/download/v0.X.Y/solidity_0.X.Y.tar.gz
(not the "Source code" provided by github).
Command-Line Build
------------------

View File

@ -313,7 +313,6 @@ size_t Scanner::scanSingleLineDocComment()
{
LiteralScope literal(this, LITERAL_TYPE_COMMENT);
size_t endPosition = m_source->position();
advance(); //consume the last '/' at ///
skipWhitespaceExceptUnicodeLinebreak();
@ -332,6 +331,8 @@ size_t Scanner::scanSingleLineDocComment()
m_source->get(1) == '/' &&
m_source->get(2) == '/')
{
if (!m_source->isPastEndOfInput(4) && m_source->get(3) == '/')
break; // "////" is not a documentation comment
m_char = m_source->advanceAndGet(3);
if (atEndOfLine())
continue;
@ -353,7 +354,6 @@ size_t Scanner::scanSingleLineDocComment()
Token Scanner::skipMultiLineComment()
{
advance();
while (!isSourcePastEndOfInput())
{
char ch = m_char;
@ -437,6 +437,11 @@ Token Scanner::scanSlash()
return Token::Whitespace;
else if (m_char == '/')
{
advance(); //consume the last '/' at ///
// "////"
if (m_char == '/')
return skipSingleLineComment();
// doxygen style /// comment
m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source;
@ -462,11 +467,14 @@ Token Scanner::scanSlash()
advance(); //skip the closing slash
return Token::Whitespace;
}
// "/***"
if (m_char == '*')
// "/***/" may be interpreted as empty natspec or skipped; skipping is simpler
return skipMultiLineComment();
// we actually have a multiline documentation comment
Token comment;
m_skippedComments[NextNext].location.start = firstSlashPosition;
m_skippedComments[NextNext].location.source = m_source;
comment = scanMultiLineDocComment();
Token comment = scanMultiLineDocComment();
m_skippedComments[NextNext].location.end = static_cast<int>(sourcePos());
m_skippedComments[NextNext].token = comment;
if (comment == Token::Illegal)
@ -754,17 +762,16 @@ bool Scanner::isUnicodeLinebreak()
if (0x0a <= m_char && m_char <= 0x0d)
// line feed, vertical tab, form feed, carriage return
return true;
else if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85)
if (!m_source->isPastEndOfInput(1) && uint8_t(m_source->get(0)) == 0xc2 && uint8_t(m_source->get(1)) == 0x85)
// NEL - U+0085, C2 85 in utf8
return true;
else if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && (
if (!m_source->isPastEndOfInput(2) && uint8_t(m_source->get(0)) == 0xe2 && uint8_t(m_source->get(1)) == 0x80 && (
uint8_t(m_source->get(2)) == 0xa8 || uint8_t(m_source->get(2)) == 0xa9
))
// LS - U+2028, E2 80 A8 in utf8
// PS - U+2029, E2 80 A9 in utf8
return true;
else
return false;
return false;
}
Token Scanner::scanString()

View File

@ -123,11 +123,13 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
return !error;
}
bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode)
bool NameAndTypeResolver::resolveNamesAndTypes(SourceUnit& _source)
{
try
{
return resolveNamesAndTypesInternal(_node, _resolveInsideCode);
for (shared_ptr<ASTNode> const& node: _source.nodes())
if (!resolveNamesAndTypesInternal(*node, true))
return false;
}
catch (langutil::FatalError const&)
{
@ -135,6 +137,7 @@ bool NameAndTypeResolver::resolveNamesAndTypes(ASTNode& _node, bool _resolveInsi
throw; // Something is weird here, rather throw again.
return false;
}
return true;
}
bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
@ -227,13 +230,14 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
bool success = true;
setScope(contract->scope());
solAssert(!!m_currentScope, "");
solAssert(_resolveInsideCode, "");
m_globalContext.setCurrentContract(*contract);
updateDeclaration(*m_globalContext.currentSuper());
updateDeclaration(*m_globalContext.currentThis());
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
if (!resolveNamesAndTypesInternal(*baseContract, true))
success = false;
setScope(contract);
@ -254,23 +258,20 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
setScope(contract);
if (!resolveNamesAndTypes(*node, false))
if (!resolveNamesAndTypesInternal(*node, false))
success = false;
}
if (!success)
return false;
if (!_resolveInsideCode)
return success;
setScope(contract);
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
setScope(contract);
if (!resolveNamesAndTypes(*node, true))
if (!resolveNamesAndTypesInternal(*node, true))
success = false;
}
return success;

View File

@ -65,12 +65,9 @@ public:
bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr);
/// Applies the effect of import directives.
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
/// Resolves all names and types referenced from the given AST Node.
/// This is usually only called at the contract level, but with a bit of care, it can also
/// be called at deeper levels.
/// @param _resolveInsideCode if false, does not descend into nodes that contain code.
/// Resolves all names and types referenced from the given Source Node.
/// @returns false in case of error.
bool resolveNamesAndTypes(ASTNode& _node, bool _resolveInsideCode = true);
bool resolveNamesAndTypes(SourceUnit& _source);
/// Updates the given global declaration (used for "this"). Not to be used with declarations
/// that create their own scope.
/// @returns false in case of error.

View File

@ -62,9 +62,11 @@ bool TypeChecker::typeSupportedByOldABIEncoder(Type const& _type, bool _isLibrar
return true;
}
bool TypeChecker::checkTypeRequirements(ASTNode const& _contract)
bool TypeChecker::checkTypeRequirements(SourceUnit const& _source)
{
_contract.accept(*this);
m_currentSourceUnit = &_source;
_source.accept(*this);
m_currentSourceUnit = nullptr;
return Error::containsOnlyWarnings(m_errorReporter.errors());
}
@ -375,7 +377,7 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
}
if (
_function.isPublic() &&
!_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(var), _function.libraryFunction())
)
m_errorReporter.typeError(
@ -513,7 +515,7 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
else if (_variable.visibility() >= Visibility::Public)
{
FunctionType getter(_variable);
if (!_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2))
if (!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2))
{
vector<string> unsupportedTypes;
for (auto const& param: getter.parameterTypes() + getter.returnParameterTypes())
@ -624,7 +626,7 @@ bool TypeChecker::visit(EventDefinition const& _eventDef)
if (!type(*var)->interfaceType(false))
m_errorReporter.typeError(3417_error, var->location(), "Internal or recursive type is not allowed as event parameter type.");
if (
!_eventDef.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2) &&
!experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) &&
!typeSupportedByOldABIEncoder(*type(*var), false /* isLibrary */)
)
m_errorReporter.typeError(
@ -668,17 +670,18 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
{
auto ref = _inlineAssembly.annotation().externalReferences.find(&_identifier);
if (ref == _inlineAssembly.annotation().externalReferences.end())
return numeric_limits<size_t>::max();
Declaration const* declaration = ref->second.declaration;
return false;
InlineAssemblyAnnotation::ExternalIdentifierInfo& identifierInfo = ref->second;
Declaration const* declaration = identifierInfo.declaration;
solAssert(!!declaration, "");
bool requiresStorage = ref->second.isSlot || ref->second.isOffset;
bool requiresStorage = identifierInfo.isSlot || identifierInfo.isOffset;
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
{
solAssert(var->type(), "Expected variable type!");
if (var->immutable())
{
m_errorReporter.typeError(3773_error, _identifier.location, "Assembly access to immutable variables is not supported.");
return numeric_limits<size_t>::max();
return false;
}
if (var->isConstant())
{
@ -687,17 +690,17 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (var && !var->value())
{
m_errorReporter.typeError(3224_error, _identifier.location, "Constant has no value.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
m_errorReporter.typeError(6252_error, _identifier.location, "Constant variables cannot be assigned to.");
return numeric_limits<size_t>::max();
return false;
}
else if (requiresStorage)
{
m_errorReporter.typeError(6617_error, _identifier.location, "The suffixes _offset and _slot can only be used on non-constant storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (var && var->value() && !var->value()->annotation().type && !dynamic_cast<Literal const*>(var->value().get()))
{
@ -706,7 +709,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
_identifier.location,
"Constant variables with non-literal values cannot be forward referenced from inline assembly."
);
return size_t(-1);
return false;
}
else if (!var || !type(*var)->isValueType() || (
!dynamic_cast<Literal const*>(var->value().get()) &&
@ -714,7 +717,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
))
{
m_errorReporter.typeError(7615_error, _identifier.location, "Only direct number constants and references to such constants are supported by inline assembly.");
return size_t(-1);
return false;
}
}
@ -725,33 +728,33 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (!var->isStateVariable() && !var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(3622_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
if (var->isStateVariable())
{
m_errorReporter.typeError(4713_error, _identifier.location, "State variables cannot be assigned to - you have to use \"sstore()\".");
return numeric_limits<size_t>::max();
return false;
}
else if (ref->second.isOffset)
else if (identifierInfo.isOffset)
{
m_errorReporter.typeError(9739_error, _identifier.location, "Only _slot can be assigned to.");
return numeric_limits<size_t>::max();
return false;
}
else
solAssert(ref->second.isSlot, "");
solAssert(identifierInfo.isSlot, "");
}
}
else if (!var->isConstant() && var->isStateVariable())
{
m_errorReporter.typeError(1408_error, _identifier.location, "Only local variables are supported. To access storage variables, use the _slot and _offset suffixes.");
return numeric_limits<size_t>::max();
return false;
}
else if (var->type()->dataStoredIn(DataLocation::Storage))
{
m_errorReporter.typeError(9068_error, _identifier.location, "You have to use the _slot or _offset suffix to access storage reference variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (var->type()->sizeOnStack() != 1)
{
@ -759,21 +762,21 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
m_errorReporter.typeError(2370_error, _identifier.location, "Call data elements cannot be accessed directly. Copy to a local variable first or use \"calldataload\" or \"calldatacopy\" with manually determined offsets and sizes.");
else
m_errorReporter.typeError(9857_error, _identifier.location, "Only types that use one stack slot are supported.");
return numeric_limits<size_t>::max();
return false;
}
}
else if (requiresStorage)
{
m_errorReporter.typeError(7944_error, _identifier.location, "The suffixes _offset and _slot can only be used on storage variables.");
return numeric_limits<size_t>::max();
return false;
}
else if (_context == yul::IdentifierContext::LValue)
{
if (dynamic_cast<MagicVariableDeclaration const*>(declaration))
return numeric_limits<size_t>::max();
return false;
m_errorReporter.typeError(1990_error, _identifier.location, "Only local variables can be assigned to in inline assembly.");
return numeric_limits<size_t>::max();
return false;
}
if (_context == yul::IdentifierContext::RValue)
@ -782,7 +785,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (dynamic_cast<FunctionDefinition const*>(declaration))
{
m_errorReporter.declarationError(2025_error, _identifier.location, "Access to functions is not allowed in inline assembly.");
return numeric_limits<size_t>::max();
return false;
}
else if (dynamic_cast<VariableDeclaration const*>(declaration))
{
@ -792,14 +795,14 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
if (!contract->isLibrary())
{
m_errorReporter.typeError(4977_error, _identifier.location, "Expected a library.");
return numeric_limits<size_t>::max();
return false;
}
}
else
return numeric_limits<size_t>::max();
return false;
}
ref->second.valueSize = 1;
return size_t(1);
identifierInfo.valueSize = 1;
return true;
};
solAssert(!_inlineAssembly.annotation().analysisInfo, "");
_inlineAssembly.annotation().analysisInfo = make_shared<yul::AsmAnalysisInfo>();
@ -1914,9 +1917,7 @@ void TypeChecker::typeCheckABIEncodeFunctions(
bool const isPacked = _functionType->kind() == FunctionType::Kind::ABIEncodePacked;
solAssert(_functionType->padArguments() != isPacked, "ABI function with unexpected padding");
bool const abiEncoderV2 = m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
bool const abiEncoderV2 = experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2);
// Check for named arguments
if (!_functionCall.names().empty())
@ -2313,11 +2314,10 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
{
case FunctionType::Kind::ABIDecode:
{
bool const abiEncoderV2 =
m_currentContract->sourceUnit().annotation().experimentalFeatures.count(
ExperimentalFeature::ABIEncoderV2
);
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(_functionCall, abiEncoderV2);
returnTypes = typeCheckABIDecodeAndRetrieveReturnType(
_functionCall,
experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)
);
break;
}
case FunctionType::Kind::ABIEncode:
@ -3272,3 +3272,11 @@ void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAss
m_errorReporter.typeError(errorId, _expression.location(), description);
}
bool TypeChecker::experimentalFeatureActive(ExperimentalFeature _feature) const
{
solAssert(m_currentSourceUnit, "");
if (m_currentContract)
solAssert(m_currentSourceUnit == &m_currentContract->sourceUnit(), "");
return m_currentSourceUnit->annotation().experimentalFeatures.count(_feature);
}

View File

@ -51,9 +51,9 @@ public:
m_errorReporter(_errorReporter)
{}
/// Performs type checking on the given contract and all of its sub-nodes.
/// Performs type checking on the given source and all of its sub-nodes.
/// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
bool checkTypeRequirements(ASTNode const& _contract);
bool checkTypeRequirements(SourceUnit const& _source);
/// @returns the type of an expression and asserts that it is present.
TypePointer const& type(Expression const& _expression) const;
@ -165,6 +165,9 @@ private:
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
void requireLValue(Expression const& _expression, bool _ordinaryAssignment);
bool experimentalFeatureActive(ExperimentalFeature _feature) const;
SourceUnit const* m_currentSourceUnit = nullptr;
ContractDefinition const* m_currentContract = nullptr;
langutil::EVMVersion m_evmVersion;

View File

@ -311,10 +311,15 @@ TypePointer Type::commonType(Type const* _a, Type const* _b)
return nullptr;
}
MemberList const& Type::members(ContractDefinition const* _currentScope) const
MemberList const& Type::members(ASTNode const* _currentScope) const
{
if (!m_members[_currentScope])
{
solAssert(
_currentScope == nullptr ||
dynamic_cast<SourceUnit const*>(_currentScope) ||
dynamic_cast<ContractDefinition const*>(_currentScope),
"");
MemberList::MemberMap members = nativeMembers(_currentScope);
if (_currentScope)
members += boundFunctions(*this, *_currentScope);
@ -344,8 +349,20 @@ TypePointer Type::fullEncodingType(bool _inLibraryCall, bool _encoderV2, bool) c
return encodingType;
}
MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition const& _scope)
MemberList::MemberMap Type::boundFunctions(Type const& _type, ASTNode const& _scope)
{
vector<UsingForDirective const*> usingForDirectives;
if (auto const* sourceUnit = dynamic_cast<SourceUnit const*>(&_scope))
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(sourceUnit->nodes());
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(&_scope))
{
for (ContractDefinition const* contract: contract->annotation().linearizedBaseContracts)
usingForDirectives += contract->usingForDirectives();
usingForDirectives += ASTNode::filteredNodes<UsingForDirective>(contract->sourceUnit().nodes());
}
else
solAssert(false, "");
// Normalise data location of type.
DataLocation typeLocation = DataLocation::Storage;
if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
@ -353,38 +370,39 @@ MemberList::MemberMap Type::boundFunctions(Type const& _type, ContractDefinition
set<Declaration const*> seenFunctions;
MemberList::MemberMap members;
for (ContractDefinition const* contract: _scope.annotation().linearizedBaseContracts)
for (UsingForDirective const* ufd: contract->usingForDirectives())
{
// Convert both types to pointers for comparison to see if the `using for`
// directive applies.
// Further down, we check more detailed for each function if `_type` is
// convertible to the function parameter type.
if (ufd->typeName() &&
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
*TypeProvider::withLocationIfReference(
typeLocation,
ufd->typeName()->annotation().type,
true
)
for (UsingForDirective const* ufd: usingForDirectives)
{
// Convert both types to pointers for comparison to see if the `using for`
// directive applies.
// Further down, we check more detailed for each function if `_type` is
// convertible to the function parameter type.
if (ufd->typeName() &&
*TypeProvider::withLocationIfReference(typeLocation, &_type, true) !=
*TypeProvider::withLocationIfReference(
typeLocation,
ufd->typeName()->annotation().type,
true
)
)
continue;
auto const& library = dynamic_cast<ContractDefinition const&>(
*ufd->libraryName().annotation().referencedDeclaration
);
for (FunctionDefinition const* function: library.definedFunctions())
{
if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function))
continue;
auto const& library = dynamic_cast<ContractDefinition const&>(
*ufd->libraryName().annotation().referencedDeclaration
);
for (FunctionDefinition const* function: library.definedFunctions())
{
if (!function->isVisibleAsLibraryMember() || seenFunctions.count(function))
continue;
seenFunctions.insert(function);
if (function->parameters().empty())
continue;
FunctionTypePointer fun =
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction();
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
members.emplace_back(function->name(), fun, function);
}
seenFunctions.insert(function);
if (function->parameters().empty())
continue;
FunctionTypePointer fun =
dynamic_cast<FunctionType const&>(*function->typeViaContractName()).asBoundFunction();
if (_type.isImplicitlyConvertibleTo(*fun->selfType()))
members.emplace_back(function->name(), fun, function);
}
}
return members;
}
@ -464,7 +482,7 @@ bool AddressType::operator==(Type const& _other) const
return other.m_stateMutability == m_stateMutability;
}
MemberList::MemberMap AddressType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap AddressType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap members = {
{"balance", TypeProvider::uint256()},
@ -1428,7 +1446,7 @@ TypeResult FixedBytesType::binaryOperatorResult(Token _operator, Type const* _ot
return nullptr;
}
MemberList::MemberMap FixedBytesType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap FixedBytesType::nativeMembers(ASTNode const*) const
{
return MemberList::MemberMap{MemberList::Member{"length", TypeProvider::uint(8)}};
}
@ -1884,7 +1902,7 @@ string ArrayType::signatureInExternalFunction(bool _structsByName) const
}
}
MemberList::MemberMap ArrayType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap ArrayType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap members;
if (!isString())
@ -2057,10 +2075,9 @@ string ContractType::canonicalName() const
return m_contract.annotation().canonicalName;
}
MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _contract) const
MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap members;
solAssert(_contract, "");
if (m_super)
{
// add the most derived of all functions which are visible in derived contracts
@ -2091,14 +2108,13 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
}
}
else if (!m_contract.isLibrary())
{
for (auto const& it: m_contract.interfaceFunctions())
members.emplace_back(
it.second->declaration().name(),
it.second->asExternallyCallableFunction(m_contract.isLibrary()),
&it.second->declaration()
);
}
return members;
}
@ -2269,7 +2285,7 @@ string StructType::toString(bool _short) const
return ret;
}
MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap StructType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap members;
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
@ -3174,7 +3190,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
);
}
MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _scope) const
MemberList::MemberMap FunctionType::nativeMembers(ASTNode const* _scope) const
{
switch (m_kind)
{
@ -3193,7 +3209,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _sco
functionDefinition->isPartOfExternalInterface()
)
{
solAssert(_scope->derivesFrom(*functionDefinition->annotation().contract), "");
auto const* contractScope = dynamic_cast<ContractDefinition const*>(_scope);
solAssert(contractScope && contractScope->derivesFrom(*functionDefinition->annotation().contract), "");
return {{"selector", TypeProvider::fixedBytes(4)}};
}
else
@ -3668,13 +3685,14 @@ vector<tuple<string, TypePointer>> TypeType::makeStackItems() const
return {};
}
MemberList::MemberMap TypeType::nativeMembers(ContractDefinition const* _currentScope) const
MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) const
{
MemberList::MemberMap members;
if (m_actualType->category() == Category::Contract)
{
auto const* contractScope = dynamic_cast<ContractDefinition const*>(_currentScope);
ContractDefinition const& contract = dynamic_cast<ContractType const&>(*m_actualType).contractDefinition();
bool inDerivingScope = _currentScope && _currentScope->derivesFrom(contract);
bool inDerivingScope = contractScope && contractScope->derivesFrom(contract);
for (auto const* declaration: contract.declarations())
{
@ -3776,7 +3794,7 @@ bool ModuleType::operator==(Type const& _other) const
return &m_sourceUnit == &dynamic_cast<ModuleType const&>(_other).m_sourceUnit;
}
MemberList::MemberMap ModuleType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap ModuleType::nativeMembers(ASTNode const*) const
{
MemberList::MemberMap symbols;
for (auto const& symbolName: m_sourceUnit.annotation().exportedSymbols)
@ -3817,7 +3835,7 @@ bool MagicType::operator==(Type const& _other) const
return other.m_kind == m_kind;
}
MemberList::MemberMap MagicType::nativeMembers(ContractDefinition const*) const
MemberList::MemberMap MagicType::nativeMembers(ASTNode const*) const
{
switch (m_kind)
{

View File

@ -315,9 +315,9 @@ public:
/// Returns the list of all members of this type. Default implementation: no members apart from bound.
/// @param _currentScope scope in which the members are accessed.
MemberList const& members(ContractDefinition const* _currentScope) const;
MemberList const& members(ASTNode const* _currentScope) const;
/// Convenience method, returns the type of the given named member or an empty pointer if no such member exists.
TypePointer memberType(std::string const& _name, ContractDefinition const* _currentScope = nullptr) const
TypePointer memberType(std::string const& _name, ASTNode const* _currentScope = nullptr) const
{
return members(_currentScope).memberType(_name);
}
@ -361,12 +361,12 @@ public:
private:
/// @returns a member list containing all members added to this type by `using for` directives.
static MemberList::MemberMap boundFunctions(Type const& _type, ContractDefinition const& _scope);
static MemberList::MemberMap boundFunctions(Type const& _type, ASTNode const& _scope);
protected:
/// @returns the members native to this type depending on the given context. This function
/// is used (in conjunction with boundFunctions to fill m_members below.
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* /*_currentScope*/) const
virtual MemberList::MemberMap nativeMembers(ASTNode const* /*_currentScope*/) const
{
return MemberList::MemberMap();
}
@ -379,7 +379,7 @@ protected:
/// List of member types (parameterised by scape), will be lazy-initialized.
mutable std::map<ContractDefinition const*, std::unique_ptr<MemberList>> m_members;
mutable std::map<ASTNode const*, std::unique_ptr<MemberList>> m_members;
mutable std::optional<std::vector<std::tuple<std::string, TypePointer>>> m_stackItems;
mutable std::optional<size_t> m_stackSize;
};
@ -408,7 +408,7 @@ public:
bool isValueType() const override { return true; }
bool nameable() const override { return true; }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;
std::string canonicalName() const override;
@ -649,7 +649,7 @@ public:
bool nameable() const override { return true; }
std::string toString(bool) const override { return "bytes" + util::toString(m_bytes); }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
TypePointer encodingType() const override { return this; }
TypeResult interfaceType(bool) const override { return this; }
@ -786,7 +786,7 @@ public:
std::string toString(bool _short) const override;
std::string canonicalName() const override;
std::string signatureInExternalFunction(bool _structsByName) const override;
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
TypePointer encodingType() const override;
TypePointer decodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override;
@ -889,7 +889,7 @@ public:
std::string toString(bool _short) const override;
std::string canonicalName() const override;
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
Type const* encodingType() const override;
@ -949,7 +949,7 @@ public:
bool nameable() const override { return true; }
std::string toString(bool _short) const override;
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
Type const* encodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override;
@ -1220,7 +1220,7 @@ public:
bool nameable() const override;
bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
bool hasSimpleZeroValueInMemory() const override { return false; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
TypePointer encodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override;
@ -1389,7 +1389,7 @@ public:
bool canLiveOutsideStorage() const override { return false; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
MemberList::MemberMap nativeMembers(ASTNode const* _currentScope) const override;
BoolResult isExplicitlyConvertibleTo(Type const& _convertTo) const override;
protected:
@ -1441,7 +1441,7 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;
@ -1481,7 +1481,7 @@ public:
bool canBeStored() const override { return false; }
bool canLiveOutsideStorage() const override { return true; }
bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
MemberList::MemberMap nativeMembers(ASTNode const*) const override;
std::string toString(bool _short) const override;

View File

@ -384,12 +384,11 @@ void CompilerContext::appendInlineAssembly(
yul::Identifier const& _identifier,
yul::IdentifierContext,
bool _insideFunction
) -> size_t
) -> bool
{
if (_insideFunction)
return numeric_limits<size_t>::max();
auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str());
return it == _localVariables.end() ? numeric_limits<size_t>::max() : 1;
return false;
return contains(_localVariables, _identifier.name.str());
};
identifierAccess.generateCode = [&](
yul::Identifier const& _identifier,

View File

@ -326,28 +326,28 @@ bool CompilerStack::analyze()
if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName))
return false;
// This is the main name and type resolution loop. Needs to be run for every contract, because
// the special variables "this" and "super" must be set appropriately.
for (Source const* source: m_sourceOrder)
if (source->ast && !resolver.resolveNamesAndTypes(*source->ast))
return false;
// Store contract definitions.
for (Source const* source: m_sourceOrder)
if (source->ast)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
for (
ContractDefinition const* contract:
ASTNode::filteredNodes<ContractDefinition>(source->ast->nodes())
)
{
if (!resolver.resolveNamesAndTypes(*node))
return false;
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
// Note that we now reference contracts by their fully qualified names, and
// thus contracts can only conflict if declared in the same source file. This
// should already cause a double-declaration error elsewhere.
if (m_contracts.find(contract->fullyQualifiedName()) == m_contracts.end())
m_contracts[contract->fullyQualifiedName()].contract = contract;
else
solAssert(
m_errorReporter.hasErrors(),
"Contract already present (name clash?), but no error was reported."
);
}
// Note that we now reference contracts by their fully qualified names, and
// thus contracts can only conflict if declared in the same source file. This
// should already cause a double-declaration error elsewhere.
if (!m_contracts.count(contract->fullyQualifiedName()))
m_contracts[contract->fullyQualifiedName()].contract = contract;
else
solAssert(
m_errorReporter.hasErrors(),
"Contract already present (name clash?), but no error was reported."
);
}
DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion);
@ -376,11 +376,8 @@ bool CompilerStack::analyze()
// which is only done one step later.
TypeChecker typeChecker(m_evmVersion, m_errorReporter);
for (Source const* source: m_sourceOrder)
if (source->ast)
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
if (!typeChecker.checkTypeRequirements(*contract))
noErrors = false;
if (source->ast && !typeChecker.checkTypeRequirements(*source->ast))
noErrors = false;
if (noErrors)
{

View File

@ -132,17 +132,11 @@ vector<YulString> AsmAnalyzer::operator()(Identifier const& _identifier)
}
else
{
bool found = false;
if (m_resolver)
{
bool insideFunction = m_currentScope->insideFunction();
size_t stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction);
if (stackSize != numeric_limits<size_t>::max())
{
found = true;
yulAssert(stackSize == 1, "Invalid stack size of external reference.");
}
}
bool found = m_resolver && m_resolver(
_identifier,
yul::IdentifierContext::RValue,
m_currentScope->insideFunction()
);
if (!found && watcher.ok())
// Only add an error message if the callback did not do it.
m_errorReporter.declarationError(8198_error, _identifier.location, "Identifier not found.");
@ -478,12 +472,10 @@ void AsmAnalyzer::checkAssignment(Identifier const& _variable, YulString _valueT
else if (m_resolver)
{
bool insideFunction = m_currentScope->insideFunction();
size_t variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction);
if (variableSize != numeric_limits<size_t>::max())
if (m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction))
{
found = true;
variableType = &m_dialect.defaultType;
yulAssert(variableSize == 1, "Invalid stack size of external reference.");
}
}

View File

@ -118,7 +118,7 @@ enum class IdentifierContext { LValue, RValue, VariableDeclaration };
/// to inline assembly (not used in standalone assembly mode).
struct ExternalIdentifierAccess
{
using Resolver = std::function<size_t(Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>;
using Resolver = std::function<bool(Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>;
/// Resolve an external reference given by the identifier in the given context.
/// @returns the size of the value (number of stack slots) or size_t(-1) if not found.
Resolver resolve;

View File

@ -20,6 +20,8 @@
#include <libyul/backends/wasm/WasmCodeTransform.h>
#include <libyul/backends/wasm/WasmDialect.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/AsmData.h>
@ -40,7 +42,8 @@ wasm::Module WasmCodeTransform::run(Dialect const& _dialect, yul::Block const& _
{
wasm::Module module;
WasmCodeTransform transform(_dialect, _ast);
TypeInfo typeInfo(_dialect, _ast);
WasmCodeTransform transform(_dialect, _ast, typeInfo);
for (auto const& statement: _ast.statements)
{
@ -70,14 +73,18 @@ wasm::Expression WasmCodeTransform::generateMultiAssignment(
if (_variableNames.size() == 1)
return { std::move(assignment) };
allocateGlobals(_variableNames.size() - 1);
vector<wasm::Type> typesForGlobals;
for (size_t i = 1; i < _variableNames.size(); ++i)
typesForGlobals.push_back(translatedType(m_typeInfo.typeOfVariable(YulString(_variableNames[i]))));
vector<size_t> allocatedIndices = allocateGlobals(typesForGlobals);
yulAssert(allocatedIndices.size() == _variableNames.size() - 1, "");
wasm::Block block;
block.statements.emplace_back(move(assignment));
for (size_t i = 1; i < _variableNames.size(); ++i)
block.statements.emplace_back(wasm::LocalAssignment{
move(_variableNames.at(i)),
make_unique<wasm::Expression>(wasm::GlobalVariable{m_globalVariables.at(i - 1).variableName})
make_unique<wasm::Expression>(wasm::GlobalVariable{m_globalVariables.at(allocatedIndices[i - 1]).variableName})
});
return { std::move(block) };
}
@ -88,7 +95,7 @@ wasm::Expression WasmCodeTransform::operator()(VariableDeclaration const& _varDe
for (auto const& var: _varDecl.variables)
{
variableNames.emplace_back(var.name.str());
m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back(), wasm::Type::i64});
m_localVariables.emplace_back(wasm::VariableDeclaration{variableNames.back(), translatedType(var.type)});
}
if (_varDecl.value)
@ -112,8 +119,6 @@ wasm::Expression WasmCodeTransform::operator()(ExpressionStatement const& _state
wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call)
{
bool typeConversionNeeded = false;
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
{
if (_call.functionName.name.str().substr(0, 4) == "eth.")
@ -133,7 +138,6 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call)
imp.paramTypes.emplace_back(translatedType(param));
m_functionsToImport[builtin->name] = std::move(imp);
}
typeConversionNeeded = true;
}
else if (builtin->literalArguments && contains(builtin->literalArguments.value(), true))
{
@ -147,18 +151,10 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call)
return wasm::BuiltinCall{_call.functionName.name.str(), std::move(literals)};
}
else
{
wasm::BuiltinCall call{
return wasm::BuiltinCall{
_call.functionName.name.str(),
injectTypeConversionIfNeeded(visit(_call.arguments), builtin->parameters)
visit(_call.arguments)
};
if (!builtin->returns.empty() && !builtin->returns.front().empty() && builtin->returns.front() != "i64"_yulstring)
{
yulAssert(builtin->returns.front() == "i32"_yulstring, "Invalid type " + builtin->returns.front().str());
call = wasm::BuiltinCall{"i64.extend_i32_u", make_vector<wasm::Expression>(std::move(call))};
}
return {std::move(call)};
}
}
// If this function returns multiple values, then the first one will
@ -166,13 +162,7 @@ wasm::Expression WasmCodeTransform::operator()(FunctionCall const& _call)
// The values have to be used right away in an assignment or variable declaration,
// so it is handled there.
wasm::FunctionCall funCall{_call.functionName.name.str(), visit(_call.arguments)};
if (typeConversionNeeded)
// Inject type conversion if needed on the fly. This is just a temporary measure
// and can be removed once we have proper types in Yul.
return injectTypeConversionIfNeeded(std::move(funCall));
else
return {std::move(funCall)};
return wasm::FunctionCall{_call.functionName.name.str(), visit(_call.arguments)};
}
wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier)
@ -182,30 +172,40 @@ wasm::Expression WasmCodeTransform::operator()(Identifier const& _identifier)
wasm::Expression WasmCodeTransform::operator()(Literal const& _literal)
{
u256 value = valueOfLiteral(_literal);
yulAssert(value <= numeric_limits<uint64_t>::max(), "Literal too large: " + value.str());
return wasm::Literal{static_cast<uint64_t>(value)};
return makeLiteral(translatedType(_literal.type), valueOfLiteral(_literal));
}
wasm::Expression WasmCodeTransform::operator()(If const& _if)
{
// TODO converting i64 to i32 might not always be needed.
yul::Type conditionType = m_typeInfo.typeOf(*_if.condition);
vector<wasm::Expression> args;
args.emplace_back(visitReturnByValue(*_if.condition));
args.emplace_back(wasm::Literal{static_cast<uint64_t>(0)});
return wasm::If{
make_unique<wasm::Expression>(wasm::BuiltinCall{"i64.ne", std::move(args)}),
visit(_if.body.statements),
{}
};
wasm::Expression condition;
if (conditionType == "i32"_yulstring)
condition = visitReturnByValue(*_if.condition);
else if (conditionType == "i64"_yulstring)
{
vector<wasm::Expression> args;
args.emplace_back(visitReturnByValue(*_if.condition));
args.emplace_back(makeLiteral(translatedType("i64"_yulstring), 0));
// NOTE: `if` in wasm requires an i32 argument
condition = wasm::BuiltinCall{"i64.ne", std::move(args)};
}
else
yulAssert(false, "Invalid condition type");
return wasm::If{make_unique<wasm::Expression>(move(condition)), visit(_if.body.statements), {}};
}
wasm::Expression WasmCodeTransform::operator()(Switch const& _switch)
{
yul::Type expressionType = m_typeInfo.typeOf(*_switch.expression);
YulString eq_instruction = YulString(expressionType.str() + ".eq");
yulAssert(WasmDialect::instance().builtin(eq_instruction), "");
wasm::Block block;
string condition = m_nameDispenser.newName("condition"_yulstring).str();
m_localVariables.emplace_back(wasm::VariableDeclaration{condition, wasm::Type::i64});
m_localVariables.emplace_back(wasm::VariableDeclaration{condition, translatedType(expressionType)});
block.statements.emplace_back(wasm::LocalAssignment{condition, visit(*_switch.expression)});
vector<wasm::Expression>* currentBlock = &block.statements;
@ -214,7 +214,7 @@ wasm::Expression WasmCodeTransform::operator()(Switch const& _switch)
Case const& c = _switch.cases.at(i);
if (c.value)
{
wasm::BuiltinCall comparison{"i64.eq", make_vector<wasm::Expression>(
wasm::BuiltinCall comparison{eq_instruction.str(), make_vector<wasm::Expression>(
wasm::LocalVariable{condition},
visitReturnByValue(*c.value)
)};
@ -253,11 +253,15 @@ wasm::Expression WasmCodeTransform::operator()(ForLoop const& _for)
string continueLabel = newLabel();
m_breakContinueLabelNames.push({breakLabel, continueLabel});
yul::Type conditionType = m_typeInfo.typeOf(*_for.condition);
YulString eqz_instruction = YulString(conditionType.str() + ".eqz");
yulAssert(WasmDialect::instance().builtin(eqz_instruction), "");
wasm::Loop loop;
loop.labelName = newLabel();
loop.statements = visit(_for.pre.statements);
loop.statements.emplace_back(wasm::BranchIf{wasm::Label{breakLabel}, make_unique<wasm::Expression>(
wasm::BuiltinCall{"i64.eqz", make_vector<wasm::Expression>(
wasm::BuiltinCall{eqz_instruction.str(), make_vector<wasm::Expression>(
visitReturnByValue(*_for.condition)
)}
)});
@ -325,11 +329,11 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin
wasm::FunctionDefinition fun;
fun.name = _fun.name.str();
for (auto const& param: _fun.parameters)
fun.parameters.push_back({param.name.str(), wasm::Type::i64});
fun.parameters.push_back({param.name.str(), translatedType(param.type)});
for (auto const& retParam: _fun.returnVariables)
fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str(), wasm::Type::i64});
fun.locals.emplace_back(wasm::VariableDeclaration{retParam.name.str(), translatedType(retParam.type)});
if (!_fun.returnVariables.empty())
fun.returnType = wasm::Type::i64;
fun.returnType = translatedType(_fun.returnVariables[0].type);
yulAssert(m_localVariables.empty(), "");
yulAssert(m_functionBodyLabel.empty(), "");
@ -347,10 +351,15 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin
{
// First return variable is returned directly, the others are stored
// in globals.
allocateGlobals(_fun.returnVariables.size() - 1);
vector<wasm::Type> typesForGlobals;
for (size_t i = 1; i < _fun.returnVariables.size(); ++i)
typesForGlobals.push_back(translatedType(_fun.returnVariables[i].type));
vector<size_t> allocatedIndices = allocateGlobals(typesForGlobals);
yulAssert(allocatedIndices.size() == _fun.returnVariables.size() - 1, "");
for (size_t i = 1; i < _fun.returnVariables.size(); ++i)
fun.body.emplace_back(wasm::GlobalAssignment{
m_globalVariables.at(i - 1).variableName,
m_globalVariables.at(allocatedIndices[i - 1]).variableName,
make_unique<wasm::Expression>(wasm::LocalVariable{_fun.returnVariables.at(i).name.str()})
});
fun.body.emplace_back(wasm::LocalVariable{_fun.returnVariables.front().name.str()});
@ -358,52 +367,50 @@ wasm::FunctionDefinition WasmCodeTransform::translateFunction(yul::FunctionDefin
return fun;
}
wasm::Expression WasmCodeTransform::injectTypeConversionIfNeeded(wasm::FunctionCall _call) const
{
wasm::FunctionImport const& import = m_functionsToImport.at(YulString{_call.functionName});
for (size_t i = 0; i < _call.arguments.size(); ++i)
if (import.paramTypes.at(i) == wasm::Type::i32)
_call.arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector<wasm::Expression>(std::move(_call.arguments[i]))};
else
yulAssert(import.paramTypes.at(i) == wasm::Type::i64, "Invalid Wasm type");
if (import.returnType && *import.returnType != wasm::Type::i64)
{
yulAssert(*import.returnType == wasm::Type::i32, "Invalid Wasm type");
return wasm::BuiltinCall{"i64.extend_i32_u", make_vector<wasm::Expression>(std::move(_call))};
}
return {std::move(_call)};
}
vector<wasm::Expression> WasmCodeTransform::injectTypeConversionIfNeeded(
vector<wasm::Expression> _arguments,
vector<Type> const& _parameterTypes
) const
{
for (size_t i = 0; i < _arguments.size(); ++i)
if (_parameterTypes.at(i) == "i32"_yulstring)
_arguments[i] = wasm::BuiltinCall{"i32.wrap_i64", make_vector<wasm::Expression>(std::move(_arguments[i]))};
else
yulAssert(
_parameterTypes.at(i).empty() || _parameterTypes.at(i) == "i64"_yulstring,
"Unknown type " + _parameterTypes.at(i).str()
);
return _arguments;
}
string WasmCodeTransform::newLabel()
{
return m_nameDispenser.newName("label_"_yulstring).str();
}
void WasmCodeTransform::allocateGlobals(size_t _amount)
vector<size_t> WasmCodeTransform::allocateGlobals(vector<wasm::Type> const& _typesForGlobals)
{
while (m_globalVariables.size() < _amount)
m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{
m_nameDispenser.newName("global_"_yulstring).str(),
wasm::Type::i64
});
map<wasm::Type, size_t> availableGlobals;
for (wasm::GlobalVariableDeclaration const& global: m_globalVariables)
++availableGlobals[global.type];
map<wasm::Type, size_t> neededGlobals;
for (wasm::Type const& type: _typesForGlobals)
++neededGlobals[type];
for (auto [type, neededGlobalCount]: neededGlobals)
while (availableGlobals[type] < neededGlobalCount)
{
m_globalVariables.emplace_back(wasm::GlobalVariableDeclaration{
m_nameDispenser.newName("global_"_yulstring).str(),
type,
});
++availableGlobals[type];
}
vector<size_t> allocatedIndices;
map<wasm::Type, size_t> nextGlobal;
for (wasm::Type const& type: _typesForGlobals)
{
while (m_globalVariables[nextGlobal[type]].type != type)
++nextGlobal[type];
allocatedIndices.push_back(nextGlobal[type]++);
}
yulAssert(all_of(
allocatedIndices.begin(),
allocatedIndices.end(),
[this](size_t index){ return index < m_globalVariables.size(); }
), "");
yulAssert(allocatedIndices.size() == set<size_t>(allocatedIndices.begin(), allocatedIndices.end()).size(), "Indices not unique");
yulAssert(allocatedIndices.size() == _typesForGlobals.size(), "");
return allocatedIndices;
}
wasm::Type WasmCodeTransform::translatedType(yul::Type _yulType)
@ -415,3 +422,19 @@ wasm::Type WasmCodeTransform::translatedType(yul::Type _yulType)
else
yulAssert(false, "This Yul type does not have a corresponding type in Wasm.");
}
wasm::Literal WasmCodeTransform::makeLiteral(wasm::Type _type, u256 _value)
{
if (_type == wasm::Type::i32)
{
yulAssert(_value <= numeric_limits<uint32_t>::max(), "Literal too large: " + _value.str());
return wasm::Literal{static_cast<uint32_t>(_value)};
}
else if (_type == wasm::Type::i64)
{
yulAssert(_value <= numeric_limits<uint64_t>::max(), "Literal too large: " + _value.str());
return wasm::Literal{static_cast<uint64_t>(_value)};
}
else
yulAssert(false, "Invalid Wasm literal type");
}

View File

@ -24,6 +24,9 @@
#include <libyul/AsmDataForward.h>
#include <libyul/Dialect.h>
#include <libyul/optimiser/NameDispenser.h>
#include <libyul/optimiser/TypeInfo.h>
#include <libsolutil/Common.h>
#include <stack>
#include <map>
@ -56,10 +59,12 @@ public:
private:
WasmCodeTransform(
Dialect const& _dialect,
Block const& _ast
Block const& _ast,
TypeInfo& _typeInfo
):
m_dialect(_dialect),
m_nameDispenser(_dialect, _ast)
m_nameDispenser(_dialect, _ast),
m_typeInfo(_typeInfo)
{}
std::unique_ptr<wasm::Expression> visit(yul::Expression const& _expression);
@ -79,17 +84,13 @@ private:
wasm::FunctionDefinition translateFunction(yul::FunctionDefinition const& _funDef);
wasm::Expression injectTypeConversionIfNeeded(wasm::FunctionCall _call) const;
std::vector<wasm::Expression> injectTypeConversionIfNeeded(
std::vector<wasm::Expression> _arguments,
std::vector<yul::Type> const& _parameterTypes
) const;
std::string newLabel();
/// Makes sure that there are at least @a _amount global variables.
void allocateGlobals(size_t _amount);
/// Selects a subset of global variables matching specified sequence of variable types.
/// Defines more global variables of a given type if there's not enough.
std::vector<size_t> allocateGlobals(std::vector<wasm::Type> const& _typesForGlobals);
static wasm::Type translatedType(yul::Type _yulType);
static wasm::Literal makeLiteral(wasm::Type _type, u256 _value);
Dialect const& m_dialect;
NameDispenser m_nameDispenser;
@ -99,6 +100,7 @@ private:
std::map<YulString, wasm::FunctionImport> m_functionsToImport;
std::string m_functionBodyLabel;
std::stack<std::pair<std::string, std::string>> m_breakContinueLabelNames;
TypeInfo& m_typeInfo;
};
}

View File

@ -58,6 +58,7 @@
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/algorithm/string.hpp>
#ifdef _WIN32 // windows
@ -1131,6 +1132,21 @@ bool CommandLineInterface::processInput()
}
}
vector<string> const exclusiveModes = {
g_argStandardJSON,
g_argLink,
g_argAssemble,
g_argStrictAssembly,
g_argYul,
g_argImportAst,
};
if (countEnabledOptions(exclusiveModes) > 1)
{
serr() << "The following options are mutually exclusive: " << joinOptionNames(exclusiveModes) << ". ";
serr() << "Select at most one." << endl;
return false;
}
if (m_args.count(g_argStandardJSON))
{
vector<string> inputFiles;
@ -1176,6 +1192,22 @@ bool CommandLineInterface::processInput()
if (m_args.count(g_argAssemble) || m_args.count(g_argStrictAssembly) || m_args.count(g_argYul))
{
vector<string> const nonAssemblyModeOptions = {
// TODO: The list is not complete. Add more.
g_argOutputDir,
g_argGas,
g_argCombinedJson,
g_strOptimizeYul,
g_strNoOptimizeYul,
};
if (countEnabledOptions(nonAssemblyModeOptions) >= 1)
{
serr() << "The following options are invalid in assembly mode: ";
serr() << joinOptionNames(nonAssemblyModeOptions) << ". ";
serr() << "Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
return false;
}
// switch to assembly mode
m_onlyAssemble = true;
using Input = yul::AssemblyStack::Language;
@ -1183,16 +1215,6 @@ bool CommandLineInterface::processInput()
Input inputLanguage = m_args.count(g_argYul) ? Input::Yul : (m_args.count(g_argStrictAssembly) ? Input::StrictAssembly : Input::Assembly);
Machine targetMachine = Machine::EVM;
bool optimize = m_args.count(g_argOptimize);
if (m_args.count(g_strOptimizeYul))
{
serr() << "--" << g_strOptimizeYul << " is invalid in assembly mode. Use --" << g_argOptimize << " instead." << endl;
return false;
}
if (m_args.count(g_strNoOptimizeYul))
{
serr() << "--" << g_strNoOptimizeYul << " is invalid in assembly mode. Optimization is disabled by default and can be enabled with --" << g_argOptimize << "." << endl;
return false;
}
optional<string> yulOptimiserSteps;
if (m_args.count(g_strYulOptimizations))
@ -1275,6 +1297,13 @@ bool CommandLineInterface::processInput()
return assemble(inputLanguage, targetMachine, optimize, yulOptimiserSteps);
}
else if (countEnabledOptions({g_strYulDialect, g_argMachine}) >= 1)
{
serr() << "--" << g_strYulDialect << " and --" << g_argMachine << " ";
serr() << "are only valid in assembly mode." << endl;
return false;
}
if (m_args.count(g_argLink))
{
// switch to linker mode
@ -1882,4 +1911,21 @@ void CommandLineInterface::outputCompilationResults()
}
}
size_t CommandLineInterface::countEnabledOptions(vector<string> const& _optionNames) const
{
size_t count = 0;
for (string const& _option: _optionNames)
count += m_args.count(_option);
return count;
}
string CommandLineInterface::joinOptionNames(vector<string> const& _optionNames, string _separator)
{
return boost::algorithm::join(
_optionNames | boost::adaptors::transformed([](string const& _option){ return "--" + _option; }),
_separator
);
}
}

View File

@ -103,6 +103,9 @@ private:
/// @arg _json json string to be written
void createJson(std::string const& _fileName, std::string const& _json);
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", ");
bool m_error = false; ///< If true, some error occurred.
bool m_onlyAssemble = false;

View File

@ -45,7 +45,7 @@ object "object" {
Binary representation:
0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010acb01052b01017e0240420021004200200020002000200010054220200020002000420110054200a74220a710000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b3f0002402000a7200110043700002000a74208a76aada7200210043700002000a74210a76aada7200310043700002000a74218a76aada7200410043700000b0b
0061736d0100000001160460000060017e017e60057f7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010abc01052901017e0240420021004100200020002000200010054120200020002000420110054100412010000b0b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100242108621022002200042108810028421010b20010b1e01027e02402000100342208621022002200042208810038421010b20010b32000240200020011004370000200041086a20021004370000200041106a20031004370000200041186a200410043700000b0b
Text representation:
(module
@ -57,9 +57,9 @@ Text representation:
(local $_1 i64)
(block $label_
(local.set $_1 (i64.const 0))
(call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))
(call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1))
(call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32)))
(call $mstore_internal (i32.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))
(call $mstore_internal (i32.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1))
(call $eth.storageStore (i32.const 0) (i32.const 32))
)
)
@ -101,16 +101,16 @@ Text representation:
)
(func $mstore_internal
(param $pos i64)
(param $pos i32)
(param $y1 i64)
(param $y2 i64)
(param $y3 i64)
(param $y4 i64)
(block $label__4
(i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 8))))) (call $endian_swap (local.get $y2)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 16))))) (call $endian_swap (local.get $y3)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (local.get $y4)))
(i64.store (local.get $pos) (call $endian_swap (local.get $y1)))
(i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2)))
(i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3)))
(i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4)))
)
)

View File

@ -154,7 +154,7 @@ object "object" {
Binary representation:
0061736d0100000001480a60000060017e017e60027e7e017e60037e7e7e017e60047e7e7e7e017e60057e7e7e7e7e0060087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003070407020704010101050605030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020aa5090df302011f7e02404200210002402000200020002000100921012300210223012103230221040b2001210520022106200321072004210842012109200020008420002009848450ada745ada745ad210a02400340200aa745ad500d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f20108420112012848450ada745ad42005204400c030b024020052006200720082000200020004202100621132300211423012115230221160b201320148420152016848450ada745ad42005204400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a848450ada745ad42005204400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2f01037e0240200020017c2105200520027c21032005200054ada72003200554ada772ada7ad21040b2004240020030b72010b7e0240200320077c210c200c42007c210b024020022006200c200354ada7200b200c54ada772ada7ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2601047e0240200020018420022003848450ada7ad21070b20052400200624012007240220040b4901047e02402000200451ad42005204402001200551ad42005204402002200651ad42005204402003200751ad42005204404201210b0b0b0b0b0b20092400200a2401200b240220080b2d01027e024002402000200154ad21032003420151044042ffffffff0f2102052000200152ad21020b0b0b20020b960101087e02404200210c0240200020041007210d200d42005104400240200120051007210e200e42005104400240200220061007210f200f42005104402003200754ad210c05200f42015104404200210c054201210c0b0b0b05200e42015104404200210c054201210c0b0b0b05200d42015104404200210c054201210c0b0b0b200ca7ad210b0b20092400200a2401200b240220080b8f0101087e02404200200020018420028452ad4200520440000b4200200342208852ad4200520440000b4200a72003a7ada74220a710014200a7290000100c21084200a74208a76aada7290000100c21094200a74210a76aada7290000100c210a4200a74218a76aada7290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3f0002402000a72001100c3700002000a74208a76aada72002100c3700002000a74210a76aada72003100c3700002000a74218a76aada72004100c3700000b0b2500024042002000200120022003100d42202004200520062007100d4200a74220a710000b0b
0061736d0100000001480a60000060017e017e60027e7e017f60037e7e7e017e60047e7e7e7e017e60087e7e7e7e7e7e7e7e0060087e7e7e7e7e7e7e7e017e60057f7e7e7e7e0060027f7f0060037f7f7f0002310208657468657265756d0c73746f7261676553746f7265000808657468657265756d0c63616c6c44617461436f70790009030e0d0003060406020604010101070505030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00020ac3080dde02030a7e017f147e02404200210002402000200020002000100921012300210223012103230221040b20012105200221062003210720042108420121092000200084200020098484504545210a02400340200a45450d01024002402005200620072008200020002000420a1008210b2300210c2301210d2302210e0b0240200b200c200d200e1005210f2300211023012111230221120b200f201084201120128484504504400c030b024020052006200720082000200020004202100621132300211423012115230221160b2013201484201520168484504504400c030b0240200520062007200820002000200042041006211723002118230121192302211a0b20172018842019201a8484504504400c010b0b0240200520062007200820002000200020091004211b2300211c2301211d2302211e0b201b2105201c2106201d2107201e21080c000b0b20002000200020002005200620072008100e0b0b2901037e0240200020017c2105200520027c21032005200054200320055472ad21040b2004240020030b6c010b7e0240200320077c210c200c42007c210b024020022006200c200354200b200c5472ad1003210d2300210e0b200d210a024020012005200e1003210f230021100b200f2109024020002004201010032111230021120b201121080b20092400200a2401200b240220080b2401047e0240200020018420022003848450ad21070b20052400200624012007240220040b3901047e0240200020045104402001200551044020022006510440200320075104404201210b0b0b0b0b0b20092400200a2401200b240220080b2701027f024002402000200154210320034101460440417f210205200020015221020b0b0b20020b960102047e047f02404100210c0240200020041007210d200d41004604400240200120051007210e200e41004604400240200220061007210f200f41004604402003200754210c05200f41014604404100210c054101210c0b0b0b05200e41014604404100210c054101210c0b0b0b05200d41014604404100210c054101210c0b0b0b200cad210b0b20092400200a2401200b240220080b7601087e024042002000200184200284520440000b42002003422088520440000b41002003a7412010014100290000100c2108410041086a290000100c2109410041106a290000100c210a410041186a290000100c210b2008210420092105200a2106200b21070b20052400200624012007240220040b1f01017e024020004208864280fe0383200042088842ff01838421010b20010b1e01027e02402000100a421086210220022000421088100a8421010b20010b1e01027e02402000100b422086210220022000422088100b8421010b20010b3200024020002001100c370000200041086a2002100c370000200041106a2003100c370000200041186a2004100c3700000b0b2300024041002000200120022003100d41202004200520062007100d4100412010000b0b
Text representation:
(module
@ -177,7 +177,7 @@ Text representation:
(local $x_6 i64)
(local $x_7 i64)
(local $_2 i64)
(local $_3 i64)
(local $_3 i32)
(local $_4 i64)
(local $_5 i64)
(local $_6 i64)
@ -212,10 +212,10 @@ Text representation:
(local.set $x_6 (local.get $x_2))
(local.set $x_7 (local.get $x_3))
(local.set $_2 (i64.const 1))
(local.set $_3 (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_1) (local.get $_1)) (i64.or (local.get $_1) (local.get $_2))))))))))))
(local.set $_3 (i32.eqz (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_1) (local.get $_1)) (i64.or (local.get $_1) (local.get $_2)))))))
(block $label__3
(loop $label__5
(br_if $label__3 (i64.eqz (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (local.get $_3))))))
(br_if $label__3 (i32.eqz (i32.eqz (local.get $_3))))
(block $label__4
(block
(local.set $_4 (call $lt (local.get $x_4) (local.get $x_5) (local.get $x_6) (local.get $x_7) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 10)))
@ -231,7 +231,7 @@ Text representation:
(local.set $_11 (global.get $global__2))
)
(if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_8) (local.get $_9)) (i64.or (local.get $_10) (local.get $_11)))))))) (i64.const 0)) (then
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_8) (local.get $_9)) (i64.or (local.get $_10) (local.get $_11))))) (then
(br $label__3)
))
(block
@ -241,7 +241,7 @@ Text representation:
(local.set $_15 (global.get $global__2))
)
(if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_12) (local.get $_13)) (i64.or (local.get $_14) (local.get $_15)))))))) (i64.const 0)) (then
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_12) (local.get $_13)) (i64.or (local.get $_14) (local.get $_15))))) (then
(br $label__3)
))
(block
@ -251,7 +251,7 @@ Text representation:
(local.set $_19 (global.get $global__2))
)
(if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $_16) (local.get $_17)) (i64.or (local.get $_18) (local.get $_19)))))))) (i64.const 0)) (then
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $_16) (local.get $_17)) (i64.or (local.get $_18) (local.get $_19))))) (then
(br $label__4)
))
@ -286,7 +286,7 @@ Text representation:
(block $label__6
(local.set $t (i64.add (local.get $x) (local.get $y)))
(local.set $r (i64.add (local.get $t) (local.get $c)))
(local.set $r_c (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i32.or (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $t) (local.get $x)))) (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $t)))))))))
(local.set $r_c (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t) (local.get $x)) (i64.lt_u (local.get $r) (local.get $t)))))
)
(global.set $global_ (local.get $r_c))
@ -318,7 +318,7 @@ Text representation:
(local.set $t (i64.add (local.get $x4) (local.get $y4)))
(local.set $r4 (i64.add (local.get $t) (i64.const 0)))
(block
(local.set $r3_1 (call $add_carry (local.get $x3) (local.get $y3) (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i32.or (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $t) (local.get $x4)))) (i32.wrap_i64 (i64.extend_i32_u (i64.lt_u (local.get $r4) (local.get $t))))))))))
(local.set $r3_1 (call $add_carry (local.get $x3) (local.get $y3) (i64.extend_i32_u (i32.or (i64.lt_u (local.get $t) (local.get $x4)) (i64.lt_u (local.get $r4) (local.get $t))))))
(local.set $carry (global.get $global_))
)
@ -354,7 +354,7 @@ Text representation:
(local $r3 i64)
(local $r4 i64)
(block $label__8
(local.set $r4 (i64.extend_i32_u (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $x1) (local.get $x2)) (i64.or (local.get $x3) (local.get $x4))))))))
(local.set $r4 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $x1) (local.get $x2)) (i64.or (local.get $x3) (local.get $x4))))))
)
(global.set $global_ (local.get $r2))
@ -378,10 +378,10 @@ Text representation:
(local $r3 i64)
(local $r4 i64)
(block $label__9
(if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x1) (local.get $y1))) (i64.const 0)) (then
(if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x2) (local.get $y2))) (i64.const 0)) (then
(if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x3) (local.get $y3))) (i64.const 0)) (then
(if (i64.ne (i64.extend_i32_u (i64.eq (local.get $x4) (local.get $y4))) (i64.const 0)) (then
(if (i64.eq (local.get $x1) (local.get $y1)) (then
(if (i64.eq (local.get $x2) (local.get $y2)) (then
(if (i64.eq (local.get $x3) (local.get $y3)) (then
(if (i64.eq (local.get $x4) (local.get $y4)) (then
(local.set $r4 (i64.const 1))
))
))
@ -398,16 +398,16 @@ Text representation:
(func $cmp
(param $a i64)
(param $b i64)
(result i64)
(local $r i64)
(local $condition i64)
(result i32)
(local $r i32)
(local $condition i32)
(block $label__10
(block
(local.set $condition (i64.extend_i32_u (i64.lt_u (local.get $a) (local.get $b))))
(if (i64.eq (local.get $condition) (i64.const 1)) (then
(local.set $r (i64.const 4294967295))
(local.set $condition (i64.lt_u (local.get $a) (local.get $b)))
(if (i32.eq (local.get $condition) (i32.const 1)) (then
(local.set $r (i32.const 4294967295))
)(else
(local.set $r (i64.extend_i32_u (i64.ne (local.get $a) (local.get $b))))
(local.set $r (i64.ne (local.get $a) (local.get $b)))
))
)
@ -430,50 +430,50 @@ Text representation:
(local $z2 i64)
(local $z3 i64)
(local $z4 i64)
(local $z i64)
(local $condition_12 i64)
(local $condition_13 i64)
(local $condition_14 i64)
(local $z i32)
(local $condition_12 i32)
(local $condition_13 i32)
(local $condition_14 i32)
(block $label__11
(local.set $z (i64.const 0))
(local.set $z (i32.const 0))
(block
(local.set $condition_12 (call $cmp (local.get $x1) (local.get $y1)))
(if (i64.eq (local.get $condition_12) (i64.const 0)) (then
(if (i32.eq (local.get $condition_12) (i32.const 0)) (then
(block
(local.set $condition_13 (call $cmp (local.get $x2) (local.get $y2)))
(if (i64.eq (local.get $condition_13) (i64.const 0)) (then
(if (i32.eq (local.get $condition_13) (i32.const 0)) (then
(block
(local.set $condition_14 (call $cmp (local.get $x3) (local.get $y3)))
(if (i64.eq (local.get $condition_14) (i64.const 0)) (then
(local.set $z (i64.extend_i32_u (i64.lt_u (local.get $x4) (local.get $y4))))
(if (i32.eq (local.get $condition_14) (i32.const 0)) (then
(local.set $z (i64.lt_u (local.get $x4) (local.get $y4)))
)(else
(if (i64.eq (local.get $condition_14) (i64.const 1)) (then
(local.set $z (i64.const 0))
(if (i32.eq (local.get $condition_14) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i64.const 1))
(local.set $z (i32.const 1))
))
))
)
)(else
(if (i64.eq (local.get $condition_13) (i64.const 1)) (then
(local.set $z (i64.const 0))
(if (i32.eq (local.get $condition_13) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i64.const 1))
(local.set $z (i32.const 1))
))
))
)
)(else
(if (i64.eq (local.get $condition_12) (i64.const 1)) (then
(local.set $z (i64.const 0))
(if (i32.eq (local.get $condition_12) (i32.const 1)) (then
(local.set $z (i32.const 0))
)(else
(local.set $z (i64.const 1))
(local.set $z (i32.const 1))
))
))
)
(local.set $z4 (i64.extend_i32_u (i32.wrap_i64 (local.get $z))))
(local.set $z4 (i64.extend_i32_u (local.get $z)))
)
(global.set $global_ (local.get $z2))
@ -497,15 +497,15 @@ Text representation:
(local $z3_1 i64)
(local $z4_1 i64)
(block $label__15
(if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then
(if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then
(unreachable)))
(if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then
(if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then
(unreachable)))
(call $eth.callDataCopy (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.extend_i32_u (i32.wrap_i64 (local.get $x4)))) (i32.wrap_i64 (i64.const 32)))
(local.set $z1_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.const 0)))))
(local.set $z2_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 8))))))))
(local.set $z3_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 16))))))))
(local.set $z4_1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 24))))))))
(call $eth.callDataCopy (i32.const 0) (i32.wrap_i64 (local.get $x4)) (i32.const 32))
(local.set $z1_1 (call $endian_swap (i64.load (i32.const 0))))
(local.set $z2_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8)))))
(local.set $z3_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16)))))
(local.set $z4_1 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))
(local.set $z1 (local.get $z1_1))
(local.set $z2 (local.get $z2_1))
(local.set $z3 (local.get $z3_1))
@ -556,16 +556,16 @@ Text representation:
)
(func $mstore_internal
(param $pos i64)
(param $pos i32)
(param $y1 i64)
(param $y2 i64)
(param $y3 i64)
(param $y4 i64)
(block $label__19
(i64.store (i32.wrap_i64 (local.get $pos)) (call $endian_swap (local.get $y1)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 8))))) (call $endian_swap (local.get $y2)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 16))))) (call $endian_swap (local.get $y3)))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $pos)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (local.get $y4)))
(i64.store (local.get $pos) (call $endian_swap (local.get $y1)))
(i64.store (i32.add (local.get $pos) (i32.const 8)) (call $endian_swap (local.get $y2)))
(i64.store (i32.add (local.get $pos) (i32.const 16)) (call $endian_swap (local.get $y3)))
(i64.store (i32.add (local.get $pos) (i32.const 24)) (call $endian_swap (local.get $y4)))
)
)
@ -579,9 +579,9 @@ Text representation:
(param $y3 i64)
(param $y4 i64)
(block $label__20
(call $mstore_internal (i64.const 0) (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))
(call $mstore_internal (i64.const 32) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))
(call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32)))
(call $mstore_internal (i32.const 0) (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))
(call $mstore_internal (i32.const 32) (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))
(call $eth.storageStore (i32.const 0) (i32.const 32))
)
)

View File

@ -9,8 +9,8 @@
(func $main
(local $_1 i64)
(local $p i64)
(local $r i64)
(local $p i32)
(local $r i32)
(local $_2 i64)
(local $z1 i64)
(local $z2 i64)
@ -19,19 +19,19 @@
(block $label_
(local.set $_1 (i64.const 0))
(local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64)))
(local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64)))))
(if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then
(local.set $r (i32.add (local.get $p) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $p)) (then
(unreachable)))
(local.set $_2 (call $endian_swap (local.get $_1)))
(i64.store (i32.wrap_i64 (local.get $r)) (local.get $_2))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 8))))) (local.get $_2))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 16))))) (local.get $_2))
(i64.store (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (i64.const 24))))) (call $endian_swap (i64.const 128)))
(call $eth.getCallValue (i32.wrap_i64 (i64.const 0)))
(local.set $z1 (call $endian_swap (i64.load (i32.wrap_i64 (i64.const 0)))))
(local.set $z2 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 8))))))))
(local.set $z3 (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 16))))))))
(if (i64.ne (i64.extend_i32_u (i32.eqz (i32.wrap_i64 (i64.extend_i32_u (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.wrap_i64 (i64.extend_i32_u (i32.add (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 24)))))))))))))) (i64.const 0)) (then
(i64.store (local.get $r) (local.get $_2))
(i64.store (i32.add (local.get $r) (i32.const 8)) (local.get $_2))
(i64.store (i32.add (local.get $r) (i32.const 16)) (local.get $_2))
(i64.store (i32.add (local.get $r) (i32.const 24)) (call $endian_swap (i64.const 128)))
(call $eth.getCallValue (i32.const 0))
(local.set $z1 (call $endian_swap (i64.load (i32.const 0))))
(local.set $z2 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 8)))))
(local.set $z3 (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 16)))))
(if (i32.eqz (i64.eqz (i64.or (i64.or (local.get $z1) (local.get $z2)) (i64.or (local.get $z3) (call $endian_swap (i64.load (i32.add (i32.const 0) (i32.const 24)))))))) (then
(call $revert (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))))
(local.set $_3 (datasize \"C_2_deployed\"))
(call $codecopy (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\") (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_3))
@ -44,14 +44,14 @@
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $v i64)
(result i32)
(local $v i32)
(block $label__1
(if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3)))) (i64.const 0)) (then
(if (i64.ne (i64.const 0) (i64.or (i64.or (local.get $x1) (local.get $x2)) (local.get $x3))) (then
(unreachable)))
(if (i64.ne (i64.extend_i32_u (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32)))) (i64.const 0)) (then
(if (i64.ne (i64.const 0) (i64.shr_u (local.get $x4) (i64.const 32))) (then
(unreachable)))
(local.set $v (i64.extend_i32_u (i32.wrap_i64 (local.get $x4))))
(local.set $v (i32.wrap_i64 (local.get $x4)))
)
(local.get $v)
@ -62,13 +62,13 @@
(param $x2 i64)
(param $x3 i64)
(param $x4 i64)
(result i64)
(local $r i64)
(local $p i64)
(result i32)
(local $r i32)
(local $p i32)
(block $label__2
(local.set $p (call $u256_to_i32 (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)))
(local.set $r (i64.extend_i32_u (i32.add (i32.wrap_i64 (local.get $p)) (i32.wrap_i64 (i64.const 64)))))
(if (i64.ne (i64.extend_i32_u (i32.lt_u (i32.wrap_i64 (local.get $r)) (i32.wrap_i64 (local.get $p)))) (i64.const 0)) (then
(local.set $r (i32.add (local.get $p) (i32.const 64)))
(if (i32.lt_u (local.get $r) (local.get $p)) (then
(unreachable)))
)
@ -89,7 +89,7 @@
(param $z3 i64)
(param $z4 i64)
(block $label__3
(call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4))))
(call $eth.codeCopy (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)) (call $u256_to_i32 (local.get $z1) (local.get $z2) (local.get $z3) (local.get $z4)))
)
)
@ -140,7 +140,7 @@
(param $y3 i64)
(param $y4 i64)
(block $label__7
(call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))))
(call $eth.finish (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
)
)
@ -154,7 +154,7 @@
(param $y3 i64)
(param $y4 i64)
(block $label__8
(call $eth.revert (i32.wrap_i64 (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4))) (i32.wrap_i64 (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4))))
(call $eth.revert (call $to_internal_i32ptr (local.get $x1) (local.get $x2) (local.get $x3) (local.get $x4)) (call $u256_to_i32 (local.get $y1) (local.get $y2) (local.get $y3) (local.get $y4)))
)
)

View File

@ -0,0 +1 @@
--strict-assembly --output-dir /tmp/

View File

@ -0,0 +1 @@
The following options are invalid in assembly mode: --output-dir, --gas, --combined-json, --optimize-yul, --no-optimize-yul. Optimization is disabled by default and can be enabled with --optimize.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
{
sstore(0, 1)
}

View File

@ -0,0 +1 @@
--yul-dialect evm --machine ewasm

View File

@ -0,0 +1 @@
--yul-dialect and --machine are only valid in assembly mode.

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.0;
contract C
{
function f() public pure {}
}

View File

@ -20,24 +20,24 @@ object "object" {
Binary representation:
0061736d01000000010c0260000060047e7e7e7e017e020100030302000105030100010610037e0142000b7e0142000b7e0142000b071102066d656d6f72790200046d61696e00000a4a022201047e024002404201420242034204100121002300210123012102230221030b0b0b2501047e0240200121042002210720002105200321060b20052400200624012007240220040b
0061736d01000000010c0260000060047f7e7e7f017e020100030302000105030100010610037f0141000b7f0141000b7e0142000b071102066d656d6f72790200046d61696e00000a52022603017e027f017e024002404101420242034104100121002300210123012102230221030b0b0b2903017e027f017e0240200121042002210720002105200321060b20052400200624012007240220040b
Text representation:
(module
(memory $memory (export "memory") 1)
(export "main" (func $main))
(global $global_ (mut i64) (i64.const 0))
(global $global__1 (mut i64) (i64.const 0))
(global $global_ (mut i32) (i32.const 0))
(global $global__1 (mut i32) (i32.const 0))
(global $global__2 (mut i64) (i64.const 0))
(func $main
(local $m i64)
(local $n i64)
(local $p i64)
(local $n i32)
(local $p i32)
(local $q i64)
(block $label_
(block
(local.set $m (call $multireturn (i64.const 1) (i64.const 2) (i64.const 3) (i64.const 4)))
(local.set $m (call $multireturn (i32.const 1) (i64.const 2) (i64.const 3) (i32.const 4)))
(local.set $n (global.get $global_))
(local.set $p (global.get $global__1))
(local.set $q (global.get $global__2))
@ -48,14 +48,14 @@ Text representation:
)
(func $multireturn
(param $a i64)
(param $a i32)
(param $b i64)
(param $c i64)
(param $d i64)
(param $d i32)
(result i64)
(local $x i64)
(local $y i64)
(local $z i64)
(local $y i32)
(local $z i32)
(local $w i64)
(block $label__3
(local.set $x (local.get $b))

View File

@ -11,7 +11,7 @@ object "object" {
Binary representation:
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0f010d0002404201a7422a3c00000b0b
0061736d01000000010401600000020100030201000503010001060100071102066d656d6f72790200046d61696e00000a0e010c0002404101422a3c00000b0b
Text representation:
(module
@ -20,7 +20,7 @@ Text representation:
(func $main
(block $label_
(i64.store8 (i32.wrap_i64 (i64.const 1)) (i64.const 42))
(i64.store8 (i32.const 1) (i64.const 42))
)
)

View File

@ -63,27 +63,19 @@ evmasm::AssemblyItems compileContract(std::shared_ptr<CharStream> _sourceCode)
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
solAssert(Error::containsOnlyWarnings(errorReporter.errors()), "");
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
}
BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*sourceUnit));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
{
BOOST_REQUIRE_NO_THROW(declarationTypeChecker.check(*node));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
}
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter);
BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
}
TypeChecker checker(solidity::test::CommonOptions::get().evmVersion(), errorReporter);
BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*sourceUnit));
if (!Error::containsOnlyWarnings(errorReporter.errors()))
return AssemblyItems();
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{

View File

@ -118,19 +118,12 @@ bytes compileFirstExpression(
GlobalContext globalContext;
NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), errorReporter);
resolver.registerDeclarations(*sourceUnit);
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*contract), "Resolving names failed");
BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*sourceUnit), "Resolving names failed");
DeclarationTypeChecker declarationTypeChecker(errorReporter, solidity::test::CommonOptions::get().evmVersion());
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
BOOST_REQUIRE(declarationTypeChecker.check(*node));
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{
ErrorReporter errorReporter(errors);
TypeChecker typeChecker(solidity::test::CommonOptions::get().evmVersion(), errorReporter);
BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract));
}
TypeChecker typeChecker(solidity::test::CommonOptions::get().evmVersion(), errorReporter);
BOOST_REQUIRE(typeChecker.checkTypeRequirements(*sourceUnit));
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
{

View File

@ -1150,6 +1150,74 @@ BOOST_AUTO_TEST_CASE(dev_constructor_and_function)
checkNatspec(sourceCode, "test", natspec, false);
}
BOOST_AUTO_TEST_CASE(slash4)
{
char const* sourceCode = R"(
contract test {
//// @notice lorem ipsum
function f() public { }
}
)";
char const* natspec = R"( { "methods": {} } )";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_CASE(star3)
{
char const* sourceCode = R"(
contract test {
/***
* @notice lorem ipsum
*/
function f() public { }
}
)";
char const* natspec = R"( { "methods": {} } )";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_CASE(slash3_slash3)
{
char const* sourceCode = R"(
contract test {
/// @notice lorem
/// ipsum
function f() public { }
}
)";
char const* natspec = R"ABCDEF({
"methods": {
"f()": { "notice": "lorem ipsum" }
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_CASE(slash3_slash4)
{
char const* sourceCode = R"(
contract test {
/// @notice lorem
//// ipsum
function f() public { }
}
)";
char const* natspec = R"ABCDEF({
"methods": {
"f()": { "notice": "lorem" }
}
})ABCDEF";
checkNatspec(sourceCode, "test", natspec, true);
}
BOOST_AUTO_TEST_SUITE_END()
}

View File

@ -0,0 +1,12 @@
contract C {
function f(bytes calldata b) public returns (bool correct) {
byte a = b[3];
uint r;
assembly {
r := a
}
correct = r == (0x64 << 248);
}
}
// ----
// f(bytes): 0x20, 0x04, "dead" -> true

View File

@ -0,0 +1,12 @@
contract C {
function f(int16[] calldata a) external returns (bool correct) {
uint32 x = uint32(a[1]);
uint r;
assembly {
r := x
}
correct = r == 0x7fff;
}
}
// ----
// f(int16[]): 0x20, 0x02, 0x7fff, 0x7fff -> true

View File

@ -0,0 +1,16 @@
contract C {
bytes x;
function f() public returns (uint r) {
bytes memory m = "tmp";
assembly {
mstore(m, 8)
mstore(add(m, 32), "deadbeef15dead")
}
x = m;
assembly {
r := sload(x_slot)
}
}
}
// ----
// f() -> 0x6465616462656566313564656164000000000000000000000000000000000010

View File

@ -0,0 +1,18 @@
pragma experimental ABIEncoderV2;
contract C {
struct S {
uint16[] m;
}
function f(S calldata s) public pure returns (bool correct) {
int8 x = int8(s.m[0]);
uint r;
assembly {
r := x
}
correct = r == 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80;
}
}
// ====
// compileViaYul: true
// ----
// f((uint16[])): 0x20, 0x20, 0x01, 0x0180 -> true

View File

@ -0,0 +1,18 @@
contract C {
function f() public pure returns (bool correct) {
uint8[] memory m = new uint8[](1);
assembly {
mstore(add(m, 32), 258)
}
uint8 x = m[0];
uint r;
assembly {
r := x
}
correct = (m[0] == 0x02) && (r == 0x02);
}
}
// ====
// compileViaYul: true
// ----
// f() -> true

View File

@ -0,0 +1,18 @@
contract C {
function f() public pure returns (bool correct) {
uint256[1] memory m;
assembly {
mstore(m, 0xdeadbeef15dead)
}
int32 x = int32(m[0]);
uint r;
assembly {
r := x
}
correct = (m[0] == 0xdeadbeef15dead) && (r == (((2 ** 224 - 1) << 32) | 0xef15dead));
}
}
// ====
// compileViaYul: true
// ----
// f() -> true

View File

@ -1,15 +0,0 @@
contract C {
function f() public pure returns (uint8 x, bool a, bool b) {
uint8[1] memory m;
assembly {
mstore(m, 257)
}
x = m[0];
a = (m[0] == 0x01);
b = (m[0] == 0x0101);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 1, true, false

View File

@ -0,0 +1,18 @@
contract C {
function f() public pure returns (bool correct) {
uint8[1] memory m;
assembly {
mstore(m, 257)
}
uint8 x = m[0];
uint r;
assembly {
r := x
}
correct = (m[0] == 0x01) && (r == 0x01);
}
}
// ====
// compileViaYul: true
// ----
// f() -> true

View File

@ -0,0 +1,22 @@
contract C {
struct S {
uint8[] m;
}
function f() public pure returns (bool correct) {
S memory s;
s.m = new uint8[](1);
assembly {
mstore(add(s, 64), 257)
}
uint8 x = s.m[0];
uint r;
assembly {
r := x
}
correct = r == 0x01;
}
}
// ====
// compileViaYul: true
// ----
// f() -> true

View File

@ -0,0 +1,18 @@
contract C {
function f() public pure returns (bool correct) {
uint256[1] memory m;
assembly {
mstore(m, 0xdeadbeef15dead)
}
uint32 x = uint32(m[0]);
uint r;
assembly {
r := x
}
correct = (r == 0xef15dead) && (m[0] == 0xdeadbeef15dead);
}
}
// ====
// compileViaYul: true
// ----
// f() -> true

View File

@ -0,0 +1,18 @@
contract C {
bytes b;
function f() public returns (bool correct) {
assembly {
sstore(b_slot, or("deadbeef", 0x08))
}
byte s = b[3];
uint r;
assembly {
r := s
}
correct = r == (0x64 << 248);
}
}
// ====
// compileViaYul: also
// ----
// f() -> true

View File

@ -0,0 +1,20 @@
contract C {
bytes b;
function f() public returns (bool correct) {
assembly {
sstore(b_slot, 0x41)
mstore(0, b_slot)
sstore(keccak256(0, 0x20), "deadbeefdeadbeefdeadbeefdeadbeef")
}
byte s = b[31];
uint r;
assembly {
r := s
}
correct = r == (0x66 << 248);
}
}
// ====
// compileViaYul: also
// ----
// f() -> true

View File

@ -0,0 +1,20 @@
contract C {
uint8[] s;
function f() public returns (bool correct) {
s.push();
assembly {
mstore(0, s_slot)
sstore(keccak256(0, 0x20), 257)
}
uint8 x = s[0];
uint r;
assembly {
r := x
}
correct = (s[0] == 0x01) && (r == 0x01);
}
}
// ====
// compileViaYul: also
// ----
// f() -> true

View File

@ -0,0 +1,18 @@
contract C {
uint8[1] s;
function f() public returns (bool correct) {
assembly {
sstore(s_slot, 257)
}
uint8 x = s[0];
uint r;
assembly {
r := x
}
correct = (s[0] == 0x01) && (r == 0x01);
}
}
// ====
// compileViaYul: also
// ----
// f() -> true

View File

@ -0,0 +1,23 @@
contract C {
struct S {
uint8[] m;
}
S s;
function f() public returns (bool correct) {
s.m.push();
assembly {
mstore(0, s_slot)
sstore(keccak256(0, 0x20), 257)
}
uint8 x = s.m[0];
uint r;
assembly {
r := x
}
correct = r == 0x01;
}
}
// ====
// compileViaYul: also
// ----
// f() -> true