mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into HEAD
This commit is contained in:
commit
14352ed921
Changelog.md
docs
liblangutil
libsolidity
analysis
DeclarationTypeChecker.cppDocStringAnalyser.cppDocStringTagParser.cppNameAndTypeResolver.cppPostTypeChecker.cppPostTypeChecker.hTypeChecker.cpp
ast
codegen
ABIFunctions.hExpressionCompiler.cppMultiUseYulFunctionCollector.cppYulUtilFunctions.cppYulUtilFunctions.h
ir
interface
parsing
libyul/optimiser
test
cmdlineTests
libsolidity
errorRecoveryTests
semanticTests
array
array_copy_storage_abi_signed.solarray_copy_storage_to_memory.solbyte_array_pop_copy_long.solbyte_array_pop_masking_long.sol
copying
storage_memory_nested.solstorage_memory_nested_bytes.solstorage_memory_nested_from_pointer.solstorage_memory_nested_struct.solstorage_memory_packed.solstorage_memory_packed_dyn.sol
delete_on_array_of_structs.solmemory_arrays_dynamic_index_access_write.solpush_no_args_struct.solbuiltinFunctions
constants
asm_constant_file_level.solconstant_string_at_file_level.solconstants_at_file_level_referencing.solsame_constants_different_files.sol
expressions
functionCall
inlineAssembly
libraries
structs
recursive_struct_2.solrecursive_structs.solstruct_delete_member.solstruct_delete_storage.solstruct_delete_storage_with_array.solstruct_delete_struct_in_mapping.solstruct_reference.sol
various
syntaxTests
array/length
constants
constant_natspec.solconstant_natspec_user.solconstant_override.solconstant_unassigned.solconstant_virtual.solconstant_with_visibility.solconstant_with_visibility_inverted.solcross_file_cyclic.solcross_file_cyclic_modules.solfile_level_memory.solfile_level_memory_inverted.solimmutable_at_file_level.solmapping_constant.solname_clash_via_import.solnon_constant.solredefinition_cross_file.solredefinition_public_constant.solstruct_constant.sol
freeFunctions
nameAndTypeResolution
natspec
parsing
types/mapping
contract_storage_parameter_with_mapping.sollibrary_storage_parameter_with_mapping.sollibrary_storage_parameter_with_nested_mapping.sol
unexpected.sol
10
Changelog.md
10
Changelog.md
@ -7,6 +7,16 @@ Breaking Changes:
|
|||||||
|
|
||||||
### 0.7.4 (unreleased)
|
### 0.7.4 (unreleased)
|
||||||
|
|
||||||
|
Language Features:
|
||||||
|
* Constants can be defined at file level.
|
||||||
|
|
||||||
|
|
||||||
|
Compiler Features:
|
||||||
|
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
* Type Checker: Fix internal compiler error caused by storage parameters with nested mappings in libraries.
|
||||||
|
|
||||||
|
|
||||||
### 0.7.3 (2020-10-07)
|
### 0.7.3 (2020-10-07)
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
.. index:: ! constant
|
.. index:: ! constant
|
||||||
|
|
||||||
|
.. _constants:
|
||||||
|
|
||||||
**************************************
|
**************************************
|
||||||
Constant and Immutable State Variables
|
Constant and Immutable State Variables
|
||||||
**************************************
|
**************************************
|
||||||
@ -9,6 +11,8 @@ In both cases, the variables cannot be modified after the contract has been cons
|
|||||||
For ``constant`` variables, the value has to be fixed at compile-time, while
|
For ``constant`` variables, the value has to be fixed at compile-time, while
|
||||||
for ``immutable``, it can still be assigned at construction time.
|
for ``immutable``, it can still be assigned at construction time.
|
||||||
|
|
||||||
|
It is also possible to define ``constant`` variables at the file level.
|
||||||
|
|
||||||
The compiler does not reserve a storage slot for these variables, and every occurrence is
|
The compiler does not reserve a storage slot for these variables, and every occurrence is
|
||||||
replaced by the respective value.
|
replaced by the respective value.
|
||||||
|
|
||||||
@ -26,10 +30,11 @@ Not all types for constants and immutables are implemented at this time. The onl
|
|||||||
::
|
::
|
||||||
|
|
||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.7.0 <0.9.0;
|
pragma solidity >0.7.2 <0.9.0;
|
||||||
|
|
||||||
|
uint constant X = 32**22 + 8;
|
||||||
|
|
||||||
contract C {
|
contract C {
|
||||||
uint constant X = 32**22 + 8;
|
|
||||||
string constant TEXT = "abc";
|
string constant TEXT = "abc";
|
||||||
bytes32 constant MY_HASH = keccak256("abc");
|
bytes32 constant MY_HASH = keccak256("abc");
|
||||||
uint immutable decimals;
|
uint immutable decimals;
|
||||||
|
@ -7,7 +7,7 @@ options { tokenVocab=SolidityLexer; }
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* On top level, Solidity allows pragmas, import directives, and
|
* On top level, Solidity allows pragmas, import directives, and
|
||||||
* definitions of contracts, interfaces, libraries, structs and enums.
|
* definitions of contracts, interfaces, libraries, structs, enums and constants.
|
||||||
*/
|
*/
|
||||||
sourceUnit: (
|
sourceUnit: (
|
||||||
pragmaDirective
|
pragmaDirective
|
||||||
@ -16,6 +16,7 @@ sourceUnit: (
|
|||||||
| interfaceDefinition
|
| interfaceDefinition
|
||||||
| libraryDefinition
|
| libraryDefinition
|
||||||
| functionDefinition
|
| functionDefinition
|
||||||
|
| constantVariableDeclaration
|
||||||
| structDefinition
|
| structDefinition
|
||||||
| enumDefinition
|
| enumDefinition
|
||||||
)* EOF;
|
)* EOF;
|
||||||
@ -240,6 +241,17 @@ locals [boolean constantnessSet = false, boolean visibilitySet = false, boolean
|
|||||||
(Assign initialValue=expression)?
|
(Assign initialValue=expression)?
|
||||||
Semicolon;
|
Semicolon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The declaration of a constant variable.
|
||||||
|
*/
|
||||||
|
constantVariableDeclaration
|
||||||
|
:
|
||||||
|
type=typeName
|
||||||
|
Constant
|
||||||
|
name=identifier
|
||||||
|
Assign initialValue=expression
|
||||||
|
Semicolon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parameter of an event.
|
* Parameter of an event.
|
||||||
*/
|
*/
|
||||||
|
@ -5,7 +5,8 @@ Layout of a Solidity Source File
|
|||||||
Source files can contain an arbitrary number of
|
Source files can contain an arbitrary number of
|
||||||
:ref:`contract definitions<contract_structure>`, import_ directives,
|
:ref:`contract definitions<contract_structure>`, import_ directives,
|
||||||
:ref:`pragma directives<pragma>` and
|
:ref:`pragma directives<pragma>` and
|
||||||
:ref:`struct<structs>`, :ref:`enum<enums>` and :ref:`function<functions>` definitions.
|
:ref:`struct<structs>`, :ref:`enum<enums>`, :ref:`function<functions>`
|
||||||
|
and :ref:`constant variable<constants>` definitions.
|
||||||
|
|
||||||
.. index:: ! license, spdx
|
.. index:: ! license, spdx
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
|
|||||||
|
|
||||||
if (!_ref.multiline)
|
if (!_ref.multiline)
|
||||||
{
|
{
|
||||||
auto const locationLength = static_cast<size_t>(_ref.endColumn - _ref.startColumn);
|
size_t const locationLength = static_cast<size_t>(_ref.endColumn - _ref.startColumn);
|
||||||
|
|
||||||
// line 1:
|
// line 1:
|
||||||
m_stream << leftpad << ' ';
|
m_stream << leftpad << ' ';
|
||||||
@ -124,7 +124,11 @@ void SourceReferenceFormatterHuman::printSourceLocation(SourceReference const& _
|
|||||||
frameColored() << '|';
|
frameColored() << '|';
|
||||||
|
|
||||||
m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast<size_t>(_ref.startColumn)), ' ');
|
m_stream << ' ' << replaceNonTabs(text.substr(0, static_cast<size_t>(_ref.startColumn)), ' ');
|
||||||
diagColored() << replaceNonTabs(text.substr(static_cast<size_t>(_ref.startColumn), locationLength), '^');
|
diagColored() << (
|
||||||
|
locationLength == 0 ?
|
||||||
|
"^" :
|
||||||
|
replaceNonTabs(text.substr(static_cast<size_t>(_ref.startColumn), locationLength), '^')
|
||||||
|
);
|
||||||
m_stream << '\n';
|
m_stream << '\n';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -276,11 +276,17 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
if (_variable.annotation().type)
|
if (_variable.annotation().type)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_variable.isConstant() && !_variable.isStateVariable())
|
if (_variable.isFileLevelVariable() && !_variable.isConstant())
|
||||||
|
m_errorReporter.declarationError(
|
||||||
|
8342_error,
|
||||||
|
_variable.location(),
|
||||||
|
"Only constant variables are allowed at file level."
|
||||||
|
);
|
||||||
|
if (_variable.isConstant() && (!_variable.isStateVariable() && !_variable.isFileLevelVariable()))
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
1788_error,
|
1788_error,
|
||||||
_variable.location(),
|
_variable.location(),
|
||||||
"The \"constant\" keyword can only be used for state variables."
|
"The \"constant\" keyword can only be used for state variables or variables at file level."
|
||||||
);
|
);
|
||||||
if (_variable.immutable() && !_variable.isStateVariable())
|
if (_variable.immutable() && !_variable.isStateVariable())
|
||||||
m_errorReporter.declarationError(
|
m_errorReporter.declarationError(
|
||||||
@ -344,6 +350,11 @@ void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
|
|||||||
solAssert(varLoc == Location::Unspecified, "");
|
solAssert(varLoc == Location::Unspecified, "");
|
||||||
typeLoc = DataLocation::Memory;
|
typeLoc = DataLocation::Memory;
|
||||||
}
|
}
|
||||||
|
else if (_variable.isFileLevelVariable())
|
||||||
|
{
|
||||||
|
solAssert(varLoc == Location::Unspecified, "");
|
||||||
|
typeLoc = DataLocation::Memory;
|
||||||
|
}
|
||||||
else if (_variable.isStateVariable())
|
else if (_variable.isStateVariable())
|
||||||
{
|
{
|
||||||
solAssert(varLoc == Location::Unspecified, "");
|
solAssert(varLoc == Location::Unspecified, "");
|
||||||
|
@ -87,7 +87,7 @@ bool DocStringAnalyser::visit(FunctionDefinition const& _function)
|
|||||||
|
|
||||||
bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
bool DocStringAnalyser::visit(VariableDeclaration const& _variable)
|
||||||
{
|
{
|
||||||
if (!_variable.isStateVariable())
|
if (!_variable.isStateVariable() && !_variable.isFileLevelVariable())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
|
if (CallableDeclaration const* baseFunction = resolveInheritDoc(_variable.annotation().baseFunctions, _variable, _variable.annotation()))
|
||||||
|
@ -61,13 +61,13 @@ bool DocStringTagParser::visit(VariableDeclaration const& _variable)
|
|||||||
{
|
{
|
||||||
if (_variable.isStateVariable())
|
if (_variable.isStateVariable())
|
||||||
{
|
{
|
||||||
static set<string> const validPublicTags = set<string>{"dev", "notice", "return", "inheritdoc"};
|
|
||||||
static set<string> const validNonPublicTags = set<string>{"dev", "inheritdoc"};
|
|
||||||
if (_variable.isPublic())
|
if (_variable.isPublic())
|
||||||
parseDocStrings(_variable, _variable.annotation(), validPublicTags, "public state variables");
|
parseDocStrings(_variable, _variable.annotation(), {"dev", "notice", "return", "inheritdoc"}, "public state variables");
|
||||||
else
|
else
|
||||||
parseDocStrings(_variable, _variable.annotation(), validNonPublicTags, "non-public state variables");
|
parseDocStrings(_variable, _variable.annotation(), {"dev", "inheritdoc"}, "non-public state variables");
|
||||||
}
|
}
|
||||||
|
else if (_variable.isFileLevelVariable())
|
||||||
|
parseDocStrings(_variable, _variable.annotation(), {"dev"}, "file-level variables");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,9 +130,12 @@ bool NameAndTypeResolver::resolveNamesAndTypes(SourceUnit& _source)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
for (shared_ptr<ASTNode> const& node: _source.nodes())
|
for (shared_ptr<ASTNode> const& node: _source.nodes())
|
||||||
|
{
|
||||||
|
setScope(&_source);
|
||||||
if (!resolveNamesAndTypesInternal(*node, true))
|
if (!resolveNamesAndTypesInternal(*node, true))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (langutil::FatalError const&)
|
catch (langutil::FatalError const&)
|
||||||
{
|
{
|
||||||
if (m_errorReporter.errors().empty())
|
if (m_errorReporter.errors().empty())
|
||||||
|
@ -38,6 +38,13 @@ bool PostTypeChecker::check(ASTNode const& _astRoot)
|
|||||||
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PostTypeChecker::finalize()
|
||||||
|
{
|
||||||
|
for (auto& checker: m_checkers)
|
||||||
|
checker->finalize();
|
||||||
|
return Error::containsOnlyWarnings(m_errorReporter.errors());
|
||||||
|
}
|
||||||
|
|
||||||
bool PostTypeChecker::visit(ContractDefinition const& _contractDefinition)
|
bool PostTypeChecker::visit(ContractDefinition const& _contractDefinition)
|
||||||
{
|
{
|
||||||
return callVisit(_contractDefinition);
|
return callVisit(_contractDefinition);
|
||||||
@ -83,6 +90,11 @@ bool PostTypeChecker::visit(Identifier const& _identifier)
|
|||||||
return callVisit(_identifier);
|
return callVisit(_identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PostTypeChecker::visit(MemberAccess const& _memberAccess)
|
||||||
|
{
|
||||||
|
return callVisit(_memberAccess);
|
||||||
|
}
|
||||||
|
|
||||||
bool PostTypeChecker::visit(StructDefinition const& _struct)
|
bool PostTypeChecker::visit(StructDefinition const& _struct)
|
||||||
{
|
{
|
||||||
return callVisit(_struct);
|
return callVisit(_struct);
|
||||||
@ -110,14 +122,7 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
|||||||
ConstStateVarCircularReferenceChecker(ErrorReporter& _errorReporter):
|
ConstStateVarCircularReferenceChecker(ErrorReporter& _errorReporter):
|
||||||
Checker(_errorReporter) {}
|
Checker(_errorReporter) {}
|
||||||
|
|
||||||
bool visit(ContractDefinition const&) override
|
void finalize() override
|
||||||
{
|
|
||||||
solAssert(!m_currentConstVariable, "");
|
|
||||||
solAssert(m_constVariableDependencies.empty(), "");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void endVisit(ContractDefinition const&) override
|
|
||||||
{
|
{
|
||||||
solAssert(!m_currentConstVariable, "");
|
solAssert(!m_currentConstVariable, "");
|
||||||
for (auto declaration: m_constVariables)
|
for (auto declaration: m_constVariables)
|
||||||
@ -128,9 +133,12 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
|||||||
"The value of the constant " + declaration->name() +
|
"The value of the constant " + declaration->name() +
|
||||||
" has a cyclic dependency via " + identifier->name() + "."
|
" has a cyclic dependency via " + identifier->name() + "."
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
m_constVariables.clear();
|
bool visit(ContractDefinition const&) override
|
||||||
m_constVariableDependencies.clear();
|
{
|
||||||
|
solAssert(!m_currentConstVariable, "");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool visit(VariableDeclaration const& _variable) override
|
bool visit(VariableDeclaration const& _variable) override
|
||||||
@ -162,6 +170,15 @@ struct ConstStateVarCircularReferenceChecker: public PostTypeChecker::Checker
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool visit(MemberAccess const& _memberAccess) override
|
||||||
|
{
|
||||||
|
if (m_currentConstVariable)
|
||||||
|
if (auto var = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
if (var->isConstant())
|
||||||
|
m_constVariableDependencies[m_currentConstVariable].insert(var);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom)
|
VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom)
|
||||||
{
|
{
|
||||||
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
auto visitor = [&](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth)
|
||||||
|
@ -35,7 +35,7 @@ namespace solidity::frontend
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This module performs analyses on the AST that are done after type checking and assignments of types:
|
* This module performs analyses on the AST that are done after type checking and assignments of types:
|
||||||
* - whether there are circular references in constant state variables
|
* - whether there are circular references in constant variables
|
||||||
* - whether override specifiers are actually contracts
|
* - whether override specifiers are actually contracts
|
||||||
* - whether a modifier is in a function header
|
* - whether a modifier is in a function header
|
||||||
* - whether an event is used outside of an emit statement
|
* - whether an event is used outside of an emit statement
|
||||||
@ -54,6 +54,9 @@ public:
|
|||||||
{
|
{
|
||||||
Checker(langutil::ErrorReporter& _errorReporter):
|
Checker(langutil::ErrorReporter& _errorReporter):
|
||||||
m_errorReporter(_errorReporter) {}
|
m_errorReporter(_errorReporter) {}
|
||||||
|
|
||||||
|
/// Called after all source units have been visited.
|
||||||
|
virtual void finalize() {}
|
||||||
protected:
|
protected:
|
||||||
langutil::ErrorReporter& m_errorReporter;
|
langutil::ErrorReporter& m_errorReporter;
|
||||||
};
|
};
|
||||||
@ -63,6 +66,9 @@ public:
|
|||||||
|
|
||||||
bool check(ASTNode const& _astRoot);
|
bool check(ASTNode const& _astRoot);
|
||||||
|
|
||||||
|
/// Called after all source units have been visited.
|
||||||
|
bool finalize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool visit(ContractDefinition const& _contract) override;
|
bool visit(ContractDefinition const& _contract) override;
|
||||||
void endVisit(ContractDefinition const& _contract) override;
|
void endVisit(ContractDefinition const& _contract) override;
|
||||||
@ -77,6 +83,7 @@ private:
|
|||||||
bool visit(FunctionCall const& _functionCall) override;
|
bool visit(FunctionCall const& _functionCall) override;
|
||||||
|
|
||||||
bool visit(Identifier const& _identifier) override;
|
bool visit(Identifier const& _identifier) override;
|
||||||
|
bool visit(MemberAccess const& _identifier) override;
|
||||||
|
|
||||||
bool visit(StructDefinition const& _struct) override;
|
bool visit(StructDefinition const& _struct) override;
|
||||||
void endVisit(StructDefinition const& _struct) override;
|
void endVisit(StructDefinition const& _struct) override;
|
||||||
|
@ -599,8 +599,13 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
|
|||||||
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
|
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
|
||||||
{
|
{
|
||||||
auto result = referenceType->validForLocation(referenceType->location());
|
auto result = referenceType->validForLocation(referenceType->location());
|
||||||
if (result && (_variable.isConstructorParameter() || _variable.isPublicCallableParameter()))
|
if (result)
|
||||||
|
{
|
||||||
|
bool isLibraryStorageParameter = (_variable.isLibraryFunctionParameter() && referenceType->location() == DataLocation::Storage);
|
||||||
|
bool callDataCheckRequired = ((_variable.isConstructorParameter() || _variable.isPublicCallableParameter()) && !isLibraryStorageParameter);
|
||||||
|
if (callDataCheckRequired)
|
||||||
result = referenceType->validForLocation(DataLocation::CallData);
|
result = referenceType->validForLocation(DataLocation::CallData);
|
||||||
|
}
|
||||||
if (!result)
|
if (!result)
|
||||||
{
|
{
|
||||||
solAssert(!result.message().empty(), "Expected detailed error message");
|
solAssert(!result.message().empty(), "Expected detailed error message");
|
||||||
|
@ -647,6 +647,11 @@ bool VariableDeclaration::isStateVariable() const
|
|||||||
return dynamic_cast<ContractDefinition const*>(scope());
|
return dynamic_cast<ContractDefinition const*>(scope());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VariableDeclaration::isFileLevelVariable() const
|
||||||
|
{
|
||||||
|
return dynamic_cast<SourceUnit const*>(scope());
|
||||||
|
}
|
||||||
|
|
||||||
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
|
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
|
||||||
{
|
{
|
||||||
using Location = VariableDeclaration::Location;
|
using Location = VariableDeclaration::Location;
|
||||||
|
@ -967,6 +967,7 @@ public:
|
|||||||
/// Can only be called after reference resolution.
|
/// Can only be called after reference resolution.
|
||||||
bool hasReferenceOrMappingType() const;
|
bool hasReferenceOrMappingType() const;
|
||||||
bool isStateVariable() const;
|
bool isStateVariable() const;
|
||||||
|
bool isFileLevelVariable() const;
|
||||||
bool isIndexed() const { return m_isIndexed; }
|
bool isIndexed() const { return m_isIndexed; }
|
||||||
Mutability mutability() const { return m_mutability; }
|
Mutability mutability() const { return m_mutability; }
|
||||||
bool isConstant() const { return m_mutability == Mutability::Constant; }
|
bool isConstant() const { return m_mutability == Mutability::Constant; }
|
||||||
|
@ -126,7 +126,6 @@ public:
|
|||||||
/// stack slot, it takes exactly that number of values.
|
/// stack slot, it takes exactly that number of values.
|
||||||
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
|
std::string tupleDecoder(TypePointers const& _types, bool _fromMemory = false);
|
||||||
|
|
||||||
private:
|
|
||||||
struct EncodingOptions
|
struct EncodingOptions
|
||||||
{
|
{
|
||||||
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
|
/// Pad/signextend value types and bytes/string to multiples of 32 bytes.
|
||||||
@ -146,6 +145,7 @@ private:
|
|||||||
std::string toFunctionNameSuffix() const;
|
std::string toFunctionNameSuffix() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Internal encoding function that is also used by some copying routines.
|
||||||
/// @returns the name of the ABI encoding function with the given type
|
/// @returns the name of the ABI encoding function with the given type
|
||||||
/// and queues the generation of the function to the requested functions.
|
/// and queues the generation of the function to the requested functions.
|
||||||
/// @param _fromStack if false, the input value was just loaded from storage
|
/// @param _fromStack if false, the input value was just loaded from storage
|
||||||
@ -155,6 +155,7 @@ private:
|
|||||||
Type const& _targetType,
|
Type const& _targetType,
|
||||||
EncodingOptions const& _options
|
EncodingOptions const& _options
|
||||||
);
|
);
|
||||||
|
/// Internal encoding function that is also used by some copying routines.
|
||||||
/// @returns the name of a function that internally calls `abiEncodingFunction`
|
/// @returns the name of a function that internally calls `abiEncodingFunction`
|
||||||
/// but always returns the updated encoding position, even if the type is
|
/// but always returns the updated encoding position, even if the type is
|
||||||
/// statically encoded.
|
/// statically encoded.
|
||||||
@ -163,6 +164,8 @@ private:
|
|||||||
Type const& _targetType,
|
Type const& _targetType,
|
||||||
EncodingOptions const& _options
|
EncodingOptions const& _options
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private:
|
||||||
/// Part of @a abiEncodingFunction for array target type and given calldata array.
|
/// Part of @a abiEncodingFunction for array target type and given calldata array.
|
||||||
/// Uses calldatacopy and does not perform cleanup or validation and can therefore only
|
/// Uses calldatacopy and does not perform cleanup or validation and can therefore only
|
||||||
/// be used for byte arrays and arrays with the base type uint256 or bytes32.
|
/// be used for byte arrays and arrays with the base type uint256 or bytes32.
|
||||||
|
@ -1753,18 +1753,24 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
|||||||
{
|
{
|
||||||
Type::Category category = _memberAccess.annotation().type->category();
|
Type::Category category = _memberAccess.annotation().type->category();
|
||||||
solAssert(
|
solAssert(
|
||||||
|
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||||
|
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||||
category == Type::Category::TypeType ||
|
category == Type::Category::TypeType ||
|
||||||
category == Type::Category::Module ||
|
category == Type::Category::Module,
|
||||||
category == Type::Category::Function,
|
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
|
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
{
|
{
|
||||||
auto const* funDef = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
solAssert(variable->isConstant(), "");
|
||||||
solAssert(funDef && funDef->isFree(), "");
|
appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
|
||||||
|
}
|
||||||
|
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
{
|
||||||
|
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||||
|
solAssert(function && function->isFree(), "");
|
||||||
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
||||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||||
utils().pushCombinedFunctionEntryLabel(*funDef);
|
utils().pushCombinedFunctionEntryLabel(*function);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -33,8 +33,11 @@ string MultiUseYulFunctionCollector::requestedFunctions()
|
|||||||
{
|
{
|
||||||
string result;
|
string result;
|
||||||
for (auto const& f: m_requestedFunctions)
|
for (auto const& f: m_requestedFunctions)
|
||||||
|
{
|
||||||
|
solAssert(f.second != "<<STUB<<", "");
|
||||||
// std::map guarantees ascending order when iterating through its keys.
|
// std::map guarantees ascending order when iterating through its keys.
|
||||||
result += f.second;
|
result += f.second;
|
||||||
|
}
|
||||||
m_requestedFunctions.clear();
|
m_requestedFunctions.clear();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -43,9 +46,10 @@ string MultiUseYulFunctionCollector::createFunction(string const& _name, functio
|
|||||||
{
|
{
|
||||||
if (!m_requestedFunctions.count(_name))
|
if (!m_requestedFunctions.count(_name))
|
||||||
{
|
{
|
||||||
|
m_requestedFunctions[_name] = "<<STUB<<";
|
||||||
string fun = _creator();
|
string fun = _creator();
|
||||||
solAssert(!fun.empty(), "");
|
solAssert(!fun.empty(), "");
|
||||||
solAssert(fun.find("function " + _name) != string::npos, "Function not properly named.");
|
solAssert(fun.find("function " + _name + "(") != string::npos, "Function not properly named.");
|
||||||
m_requestedFunctions[_name] = std::move(fun);
|
m_requestedFunctions[_name] = std::move(fun);
|
||||||
}
|
}
|
||||||
return _name;
|
return _name;
|
||||||
|
@ -851,7 +851,6 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type)
|
|||||||
solAssert(_type.isDynamicallySized(), "");
|
solAssert(_type.isDynamicallySized(), "");
|
||||||
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!");
|
||||||
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "...");
|
||||||
solUnimplementedAssert(_type.baseType()->storageSize() == 1, "");
|
|
||||||
|
|
||||||
string functionName = "resize_array_" + _type.identifier();
|
string functionName = "resize_array_" + _type.identifier();
|
||||||
return m_functionCollector.createFunction(functionName, [&]() {
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
@ -1147,6 +1146,45 @@ string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
|
||||||
|
{
|
||||||
|
solAssert(_type.location() == DataLocation::Storage, "");
|
||||||
|
|
||||||
|
string functionName = "clear_struct_storage_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector.createFunction(functionName, [&] {
|
||||||
|
MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
|
||||||
|
vector<map<string, string>> memberSetValues;
|
||||||
|
|
||||||
|
for (auto const& member: structMembers)
|
||||||
|
{
|
||||||
|
auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
|
||||||
|
|
||||||
|
memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
|
||||||
|
<setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
|
||||||
|
)")
|
||||||
|
("setZero", storageSetToZeroFunction(*member.type))
|
||||||
|
("memberSlotDiff", memberSlotDiff.str())
|
||||||
|
("memberStorageOffset", to_string(memberStorageOffset))
|
||||||
|
.render()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) {
|
||||||
|
<#member>
|
||||||
|
<clearMember>
|
||||||
|
</member>
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("allocStruct", allocateMemoryStructFunction(_type))
|
||||||
|
("storageSize", _type.storageSize().str())
|
||||||
|
("member", memberSetValues)
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
|
||||||
{
|
{
|
||||||
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
string functionName = "array_convert_length_to_size_" + _type.identifier();
|
||||||
@ -1446,6 +1484,70 @@ string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to)
|
||||||
|
{
|
||||||
|
solAssert(_from.dataStoredIn(DataLocation::Storage), "");
|
||||||
|
solAssert(_to.dataStoredIn(DataLocation::Memory), "");
|
||||||
|
solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
|
||||||
|
if (!_from.isDynamicallySized())
|
||||||
|
solAssert(_from.length() == _to.length(), "");
|
||||||
|
|
||||||
|
string functionName = "copy_array_from_storage_to_memory_" + _from.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector.createFunction(functionName, [&]() {
|
||||||
|
if (_from.baseType()->isValueType())
|
||||||
|
{
|
||||||
|
solAssert(_from.baseType() == _to.baseType(), "");
|
||||||
|
ABIFunctions abi(m_evmVersion, m_revertStrings, m_functionCollector);
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) -> memptr {
|
||||||
|
memptr := <allocateTemp>()
|
||||||
|
let end := <encode>(slot, memptr)
|
||||||
|
mstore(<freeMemoryPointer>, end)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("allocateTemp", allocationTemporaryMemoryFunction())
|
||||||
|
(
|
||||||
|
"encode",
|
||||||
|
abi.abiEncodeAndReturnUpdatedPosFunction(_from, _to, ABIFunctions::EncodingOptions{})
|
||||||
|
)
|
||||||
|
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_to.memoryStride() == 32, "");
|
||||||
|
solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), "");
|
||||||
|
solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), "");
|
||||||
|
solAssert(!_from.isByteArray(), "");
|
||||||
|
solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, "");
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot) -> memptr {
|
||||||
|
let length := <lengthFunction>(slot)
|
||||||
|
memptr := <allocateArray>(length)
|
||||||
|
let mpos := memptr
|
||||||
|
<?dynamic>mpos := add(mpos, 0x20)</dynamic>
|
||||||
|
let spos := <arrayDataArea>(slot)
|
||||||
|
for { let i := 0 } lt(i, length) { i := add(i, 1) } {
|
||||||
|
mstore(mpos, <convert>(spos))
|
||||||
|
mpos := add(mpos, 0x20)
|
||||||
|
spos := add(spos, <baseStorageSize>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("lengthFunction", arrayLengthFunction(_from))
|
||||||
|
("allocateArray", allocateMemoryArrayFunction(_to))
|
||||||
|
("arrayDataArea", arrayDataAreaFunction(_from))
|
||||||
|
("dynamic", _to.isDynamicallySized())
|
||||||
|
("convert", conversionFunction(*_from.baseType(), *_to.baseType()))
|
||||||
|
("baseStorageSize", _from.baseType()->storageSize().str())
|
||||||
|
.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
|
string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
|
||||||
{
|
{
|
||||||
solAssert(_keyType.sizeOnStack() <= 1, "");
|
solAssert(_keyType.sizeOnStack() <= 1, "");
|
||||||
@ -2261,8 +2363,12 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
break;
|
break;
|
||||||
case DataLocation::Memory:
|
case DataLocation::Memory:
|
||||||
// Copy the array to a free position in memory, unless it is already in memory.
|
// Copy the array to a free position in memory, unless it is already in memory.
|
||||||
solUnimplementedAssert(from.location() == DataLocation::Memory, "Not implemented yet.");
|
if (from.location() == DataLocation::Memory)
|
||||||
body = "converted := value";
|
body = "converted := value";
|
||||||
|
else if (from.location() == DataLocation::CallData)
|
||||||
|
solUnimplemented("Conversion of calldata types not yet implemented.");
|
||||||
|
else
|
||||||
|
body = "converted := " + copyArrayFromStorageToMemoryFunction(from, to) + "(value)";
|
||||||
break;
|
break;
|
||||||
case DataLocation::CallData:
|
case DataLocation::CallData:
|
||||||
solUnimplemented("Conversion of calldata types not yet implemented.");
|
solUnimplemented("Conversion of calldata types not yet implemented.");
|
||||||
@ -2278,12 +2384,16 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
auto const& toStructType = dynamic_cast<StructType const &>(_to);
|
auto const& toStructType = dynamic_cast<StructType const &>(_to);
|
||||||
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
|
solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
|
||||||
|
|
||||||
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
if (fromStructType.location() == toStructType.location() && toStructType.isPointer())
|
||||||
|
body = "converted := value";
|
||||||
|
else
|
||||||
|
{
|
||||||
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
solUnimplementedAssert(toStructType.location() == DataLocation::Memory, "");
|
||||||
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, "");
|
solUnimplementedAssert(fromStructType.location() != DataLocation::Memory, "");
|
||||||
|
|
||||||
if (fromStructType.location() == DataLocation::CallData)
|
if (fromStructType.location() == DataLocation::CallData)
|
||||||
{
|
{
|
||||||
|
solUnimplementedAssert(!fromStructType.isDynamicallyEncoded(), "");
|
||||||
body = Whiskers(R"(
|
body = Whiskers(R"(
|
||||||
converted := <abiDecode>(value, calldatasize())
|
converted := <abiDecode>(value, calldatasize())
|
||||||
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
|
)")("abiDecode", ABIFunctions(m_evmVersion, m_revertStrings, m_functionCollector).tupleDecoder(
|
||||||
@ -2300,6 +2410,7 @@ string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
|
|||||||
("readFromStorage", readFromStorage(toStructType, 0, true))
|
("readFromStorage", readFromStorage(toStructType, 0, true))
|
||||||
.render();
|
.render();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2441,7 +2552,7 @@ string YulUtilFunctions::cleanupFunction(Type const& _type)
|
|||||||
case Type::Category::Enum:
|
case Type::Category::Enum:
|
||||||
{
|
{
|
||||||
// Out of range enums cannot be truncated unambigiously and therefore it should be an error.
|
// Out of range enums cannot be truncated unambigiously and therefore it should be an error.
|
||||||
templ("body", "cleaned := value " + validatorFunction(_type) + "(value)");
|
templ("body", "cleaned := value " + validatorFunction(_type, false) + "(value)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::Category::InaccessibleDynamic:
|
case Type::Category::InaccessibleDynamic:
|
||||||
@ -2741,11 +2852,24 @@ string YulUtilFunctions::storageSetToZeroFunction(Type const& _type)
|
|||||||
else if (_type.category() == Type::Category::Array)
|
else if (_type.category() == Type::Category::Array)
|
||||||
return Whiskers(R"(
|
return Whiskers(R"(
|
||||||
function <functionName>(slot, offset) {
|
function <functionName>(slot, offset) {
|
||||||
|
if iszero(eq(offset, 0)) { <panic>() }
|
||||||
<clearArray>(slot)
|
<clearArray>(slot)
|
||||||
}
|
}
|
||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
|
||||||
|
("panic", panicFunction())
|
||||||
|
.render();
|
||||||
|
else if (_type.category() == Type::Category::Struct)
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(slot, offset) {
|
||||||
|
if iszero(eq(offset, 0)) { <panic>() }
|
||||||
|
<clearStruct>(slot)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
|
||||||
|
("panic", panicFunction())
|
||||||
.render();
|
.render();
|
||||||
else
|
else
|
||||||
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
|
||||||
@ -2933,7 +3057,7 @@ string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromC
|
|||||||
)")
|
)")
|
||||||
("functionName", functionName)
|
("functionName", functionName)
|
||||||
("fromCalldata", _fromCalldata)
|
("fromCalldata", _fromCalldata)
|
||||||
("validate", validatorFunction(_type))
|
("validate", validatorFunction(_type, true))
|
||||||
// Byte array elements generally need cleanup.
|
// Byte array elements generally need cleanup.
|
||||||
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
|
// Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
|
||||||
("cleanup", cleanupFunction(_type))
|
("cleanup", cleanupFunction(_type))
|
||||||
|
@ -220,6 +220,10 @@ public:
|
|||||||
/// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots.
|
/// Only works for memory arrays, calldata arrays and storage arrays that every item occupies one or multiple full slots.
|
||||||
std::string nextArrayElementFunction(ArrayType const& _type);
|
std::string nextArrayElementFunction(ArrayType const& _type);
|
||||||
|
|
||||||
|
/// @returns the name of a function that allocates a memory array and copies the contents
|
||||||
|
/// of the storage array into it.
|
||||||
|
std::string copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to);
|
||||||
|
|
||||||
/// @returns the name of a function that performs index access for mappings.
|
/// @returns the name of a function that performs index access for mappings.
|
||||||
/// @param _mappingType the type of the mapping
|
/// @param _mappingType the type of the mapping
|
||||||
/// @param _keyType the type of the value provided
|
/// @param _keyType the type of the value provided
|
||||||
@ -343,7 +347,7 @@ public:
|
|||||||
/// otherwise an assertion failure.
|
/// otherwise an assertion failure.
|
||||||
///
|
///
|
||||||
/// This is used for data decoded from external sources.
|
/// This is used for data decoded from external sources.
|
||||||
std::string validatorFunction(Type const& _type, bool _revertOnFailure = false);
|
std::string validatorFunction(Type const& _type, bool _revertOnFailure);
|
||||||
|
|
||||||
std::string packedHashFunction(std::vector<Type const*> const& _givenTypes, std::vector<Type const*> const& _targetTypes);
|
std::string packedHashFunction(std::vector<Type const*> const& _givenTypes, std::vector<Type const*> const& _targetTypes);
|
||||||
|
|
||||||
@ -427,6 +431,10 @@ private:
|
|||||||
/// signature: (slot, offset)
|
/// signature: (slot, offset)
|
||||||
std::string partialClearStorageSlotFunction();
|
std::string partialClearStorageSlotFunction();
|
||||||
|
|
||||||
|
/// @returns the name of a function that will clear the given storage struct
|
||||||
|
/// signature: (slot) ->
|
||||||
|
std::string clearStorageStructFunction(StructType const& _type);
|
||||||
|
|
||||||
langutil::EVMVersion m_evmVersion;
|
langutil::EVMVersion m_evmVersion;
|
||||||
RevertStrings m_revertStrings;
|
RevertStrings m_revertStrings;
|
||||||
MultiUseYulFunctionCollector& m_functionCollector;
|
MultiUseYulFunctionCollector& m_functionCollector;
|
||||||
|
@ -1871,6 +1871,35 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
|||||||
solAssert(false, "");
|
solAssert(false, "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Type::Category::Module:
|
||||||
|
{
|
||||||
|
Type::Category category = _memberAccess.annotation().type->category();
|
||||||
|
solAssert(
|
||||||
|
dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||||
|
dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) ||
|
||||||
|
category == Type::Category::TypeType ||
|
||||||
|
category == Type::Category::Module,
|
||||||
|
""
|
||||||
|
);
|
||||||
|
if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
{
|
||||||
|
solAssert(variable->isConstant(), "");
|
||||||
|
handleVariableReference(*variable, static_cast<Expression const&>(_memberAccess));
|
||||||
|
}
|
||||||
|
else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||||
|
{
|
||||||
|
auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||||
|
solAssert(function && function->isFree(), "");
|
||||||
|
solAssert(function->functionType(true), "");
|
||||||
|
solAssert(function->functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||||
|
solAssert(funType->kind() == FunctionType::Kind::Internal, "");
|
||||||
|
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||||
|
|
||||||
|
define(_memberAccess) << to_string(function->id()) << "\n";
|
||||||
|
m_context.internalFunctionAccessed(_memberAccess, *function);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
solAssert(false, "Member access to unknown type.");
|
solAssert(false, "Member access to unknown type.");
|
||||||
}
|
}
|
||||||
@ -2130,6 +2159,10 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
{
|
{
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
else if (dynamic_cast<ImportDirective const*>(declaration))
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
solAssert(false, "Identifier type not expected in expression context.");
|
solAssert(false, "Identifier type not expected in expression context.");
|
||||||
@ -2162,7 +2195,7 @@ void IRGeneratorForStatements::handleVariableReference(
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
setLocation(_referencingExpression);
|
setLocation(_referencingExpression);
|
||||||
if (_variable.isStateVariable() && _variable.isConstant())
|
if ((_variable.isStateVariable() || _variable.isFileLevelVariable()) && _variable.isConstant())
|
||||||
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
|
define(_referencingExpression) << constantValueFunction(_variable) << "()\n";
|
||||||
else if (_variable.isStateVariable() && _variable.immutable())
|
else if (_variable.isStateVariable() && _variable.immutable())
|
||||||
setLValue(_referencingExpression, IRLValue{
|
setLValue(_referencingExpression, IRLValue{
|
||||||
|
@ -387,6 +387,8 @@ bool CompilerStack::analyze()
|
|||||||
for (Source const* source: m_sourceOrder)
|
for (Source const* source: m_sourceOrder)
|
||||||
if (source->ast && !postTypeChecker.check(*source->ast))
|
if (source->ast && !postTypeChecker.check(*source->ast))
|
||||||
noErrors = false;
|
noErrors = false;
|
||||||
|
if (!postTypeChecker.finalize())
|
||||||
|
noErrors = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that immutable variables are never read in c'tors and assigned
|
// Check that immutable variables are never read in c'tors and assigned
|
||||||
|
@ -111,7 +111,17 @@ ASTPointer<SourceUnit> Parser::parse(shared_ptr<Scanner> const& _scanner)
|
|||||||
nodes.push_back(parseFunctionDefinition(true));
|
nodes.push_back(parseFunctionDefinition(true));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/function definition.");
|
// Constant variable.
|
||||||
|
if (variableDeclarationStart() && m_scanner->peekNextToken() != Token::EOS)
|
||||||
|
{
|
||||||
|
VarDeclParserOptions options;
|
||||||
|
options.kind = VarDeclKind::FileLevel;
|
||||||
|
options.allowInitialValue = true;
|
||||||
|
nodes.push_back(parseVariableDeclaration(options));
|
||||||
|
expectToken(Token::Semicolon);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
fatalParserError(7858_error, "Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
solAssert(m_recursionDepth == 0, "");
|
solAssert(m_recursionDepth == 0, "");
|
||||||
@ -332,15 +342,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition()
|
|||||||
subNodes.push_back(parseStructDefinition());
|
subNodes.push_back(parseStructDefinition());
|
||||||
else if (currentTokenValue == Token::Enum)
|
else if (currentTokenValue == Token::Enum)
|
||||||
subNodes.push_back(parseEnumDefinition());
|
subNodes.push_back(parseEnumDefinition());
|
||||||
else if (
|
else if (variableDeclarationStart())
|
||||||
currentTokenValue == Token::Identifier ||
|
|
||||||
currentTokenValue == Token::Mapping ||
|
|
||||||
TokenTraits::isElementaryTypeName(currentTokenValue) ||
|
|
||||||
(currentTokenValue == Token::Function && m_scanner->peekNextToken() == Token::LParen)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
VarDeclParserOptions options;
|
VarDeclParserOptions options;
|
||||||
options.isStateVariable = true;
|
options.kind = VarDeclKind::State;
|
||||||
options.allowInitialValue = true;
|
options.allowInitialValue = true;
|
||||||
subNodes.push_back(parseVariableDeclaration(options));
|
subNodes.push_back(parseVariableDeclaration(options));
|
||||||
expectToken(Token::Semicolon);
|
expectToken(Token::Semicolon);
|
||||||
@ -687,10 +692,10 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
ASTPointer<TypeName> type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName();
|
ASTPointer<TypeName> type = _lookAheadArrayType ? _lookAheadArrayType : parseTypeName();
|
||||||
nodeFactory.setEndPositionFromNode(type);
|
nodeFactory.setEndPositionFromNode(type);
|
||||||
|
|
||||||
if (!_options.isStateVariable && documentation != nullptr)
|
if (_options.kind == VarDeclKind::Other && documentation != nullptr)
|
||||||
parserError(2837_error, "Only state variables can have a docstring.");
|
parserError(2837_error, "Only state variables or file-level variables can have a docstring.");
|
||||||
|
|
||||||
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.isStateVariable && m_scanner->currentToken() == Token::LBrace)
|
if (dynamic_cast<FunctionTypeName*>(type.get()) && _options.kind == VarDeclKind::State && m_scanner->currentToken() == Token::LBrace)
|
||||||
fatalParserError(
|
fatalParserError(
|
||||||
2915_error,
|
2915_error,
|
||||||
"Expected a state variable declaration. If you intended this as a fallback function "
|
"Expected a state variable declaration. If you intended this as a fallback function "
|
||||||
@ -708,7 +713,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Token token = m_scanner->currentToken();
|
Token token = m_scanner->currentToken();
|
||||||
if (_options.isStateVariable && TokenTraits::isVariableVisibilitySpecifier(token))
|
if (_options.kind == VarDeclKind::State && TokenTraits::isVariableVisibilitySpecifier(token))
|
||||||
{
|
{
|
||||||
nodeFactory.markEndPosition();
|
nodeFactory.markEndPosition();
|
||||||
if (visibility != Visibility::Default)
|
if (visibility != Visibility::Default)
|
||||||
@ -724,7 +729,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
|
|||||||
else
|
else
|
||||||
visibility = parseVisibilitySpecifier();
|
visibility = parseVisibilitySpecifier();
|
||||||
}
|
}
|
||||||
else if (_options.isStateVariable && token == Token::Override)
|
else if (_options.kind == VarDeclKind::State && token == Token::Override)
|
||||||
{
|
{
|
||||||
if (overrides)
|
if (overrides)
|
||||||
parserError(9125_error, "Override already specified.");
|
parserError(9125_error, "Override already specified.");
|
||||||
@ -1928,6 +1933,16 @@ pair<vector<ASTPointer<Expression>>, vector<ASTPointer<ASTString>>> Parser::pars
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::variableDeclarationStart()
|
||||||
|
{
|
||||||
|
Token currentToken = m_scanner->currentToken();
|
||||||
|
return
|
||||||
|
currentToken == Token::Identifier ||
|
||||||
|
currentToken == Token::Mapping ||
|
||||||
|
TokenTraits::isElementaryTypeName(currentToken) ||
|
||||||
|
(currentToken == Token::Function && m_scanner->peekNextToken() == Token::LParen);
|
||||||
|
}
|
||||||
|
|
||||||
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
|
optional<string> Parser::findLicenseString(std::vector<ASTPointer<ASTNode>> const& _nodes)
|
||||||
{
|
{
|
||||||
// We circumvent the scanner here, because it skips non-docstring comments.
|
// We circumvent the scanner here, because it skips non-docstring comments.
|
||||||
|
@ -52,13 +52,13 @@ public:
|
|||||||
private:
|
private:
|
||||||
class ASTNodeFactory;
|
class ASTNodeFactory;
|
||||||
|
|
||||||
|
enum class VarDeclKind { FileLevel, State, Other };
|
||||||
struct VarDeclParserOptions
|
struct VarDeclParserOptions
|
||||||
{
|
{
|
||||||
// This is actually not needed, but due to a defect in the C++ standard, we have to.
|
// This is actually not needed, but due to a defect in the C++ standard, we have to.
|
||||||
// https://stackoverflow.com/questions/17430377
|
// https://stackoverflow.com/questions/17430377
|
||||||
VarDeclParserOptions() {}
|
VarDeclParserOptions() {}
|
||||||
|
VarDeclKind kind = VarDeclKind::Other;
|
||||||
bool isStateVariable = false;
|
|
||||||
bool allowIndexed = false;
|
bool allowIndexed = false;
|
||||||
bool allowEmptyName = false;
|
bool allowEmptyName = false;
|
||||||
bool allowInitialValue = false;
|
bool allowInitialValue = false;
|
||||||
@ -155,6 +155,9 @@ private:
|
|||||||
///@{
|
///@{
|
||||||
///@name Helper functions
|
///@name Helper functions
|
||||||
|
|
||||||
|
/// @return true if we are at the start of a variable declaration.
|
||||||
|
bool variableDeclarationStart();
|
||||||
|
|
||||||
/// Used as return value of @see peekStatementType.
|
/// Used as return value of @see peekStatementType.
|
||||||
enum class LookAheadInfo
|
enum class LookAheadInfo
|
||||||
{
|
{
|
||||||
|
@ -66,14 +66,13 @@ struct MemoryOffsetAllocator
|
|||||||
if (unreachableVariables.count(_function))
|
if (unreachableVariables.count(_function))
|
||||||
{
|
{
|
||||||
yulAssert(!slotAllocations.count(_function), "");
|
yulAssert(!slotAllocations.count(_function), "");
|
||||||
auto& assignedSlots = slotAllocations[_function];
|
|
||||||
for (YulString variable: unreachableVariables.at(_function))
|
for (YulString variable: unreachableVariables.at(_function))
|
||||||
if (variable.empty())
|
if (variable.empty())
|
||||||
{
|
{
|
||||||
// TODO: Too many function arguments or return parameters.
|
// TODO: Too many function arguments or return parameters.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
assignedSlots[variable] = requiredSlots++;
|
slotAllocations[variable] = requiredSlots++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return slotsRequiredForFunction[_function] = requiredSlots;
|
return slotsRequiredForFunction[_function] = requiredSlots;
|
||||||
@ -82,7 +81,7 @@ struct MemoryOffsetAllocator
|
|||||||
map<YulString, set<YulString>> const& unreachableVariables;
|
map<YulString, set<YulString>> const& unreachableVariables;
|
||||||
map<YulString, set<YulString>> const& callGraph;
|
map<YulString, set<YulString>> const& callGraph;
|
||||||
|
|
||||||
map<YulString, map<YulString, uint64_t>> slotAllocations{};
|
map<YulString, uint64_t> slotAllocations{};
|
||||||
map<YulString, uint64_t> slotsRequiredForFunction{};
|
map<YulString, uint64_t> slotsRequiredForFunction{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,25 +54,22 @@ vector<Statement> generateMemoryStore(
|
|||||||
void StackToMemoryMover::run(
|
void StackToMemoryMover::run(
|
||||||
OptimiserStepContext& _context,
|
OptimiserStepContext& _context,
|
||||||
u256 _reservedMemory,
|
u256 _reservedMemory,
|
||||||
map<YulString, map<YulString, uint64_t>> const& _memorySlots,
|
map<YulString, uint64_t> const& _memorySlots,
|
||||||
uint64_t _numRequiredSlots,
|
uint64_t _numRequiredSlots,
|
||||||
Block& _block
|
Block& _block
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
StackToMemoryMover stackToMemoryMover(_context, _reservedMemory, _memorySlots, _numRequiredSlots);
|
VariableMemoryOffsetTracker memoryOffsetTracker(_reservedMemory, _memorySlots, _numRequiredSlots);
|
||||||
|
StackToMemoryMover stackToMemoryMover(_context, memoryOffsetTracker);
|
||||||
stackToMemoryMover(_block);
|
stackToMemoryMover(_block);
|
||||||
}
|
}
|
||||||
|
|
||||||
StackToMemoryMover::StackToMemoryMover(
|
StackToMemoryMover::StackToMemoryMover(
|
||||||
OptimiserStepContext& _context,
|
OptimiserStepContext& _context,
|
||||||
u256 _reservedMemory,
|
VariableMemoryOffsetTracker const& _memoryOffsetTracker
|
||||||
map<YulString, map<YulString, uint64_t>> const& _memorySlots,
|
|
||||||
uint64_t _numRequiredSlots
|
|
||||||
):
|
):
|
||||||
m_context(_context),
|
m_context(_context),
|
||||||
m_reservedMemory(std::move(_reservedMemory)),
|
m_memoryOffsetTracker(_memoryOffsetTracker),
|
||||||
m_memorySlots(_memorySlots),
|
|
||||||
m_numRequiredSlots(_numRequiredSlots),
|
|
||||||
m_nameDispenser(_context.dispenser)
|
m_nameDispenser(_context.dispenser)
|
||||||
{
|
{
|
||||||
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
auto const* evmDialect = dynamic_cast<EVMDialect const*>(&_context.dialect);
|
||||||
@ -80,41 +77,25 @@ m_nameDispenser(_context.dispenser)
|
|||||||
evmDialect && evmDialect->providesObjectAccess(),
|
evmDialect && evmDialect->providesObjectAccess(),
|
||||||
"StackToMemoryMover can only be run on objects using the EVMDialect with object access."
|
"StackToMemoryMover can only be run on objects using the EVMDialect with object access."
|
||||||
);
|
);
|
||||||
|
|
||||||
if (m_memorySlots.count(YulString{}))
|
|
||||||
// If the global scope contains variables to be moved, start with those as if it were a function.
|
|
||||||
m_currentFunctionMemorySlots = &m_memorySlots.at(YulString{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
void StackToMemoryMover::operator()(FunctionDefinition& _functionDefinition)
|
||||||
{
|
{
|
||||||
if (m_memorySlots.count(_functionDefinition.name))
|
|
||||||
{
|
|
||||||
map<YulString, uint64_t> const* saved = m_currentFunctionMemorySlots;
|
|
||||||
m_currentFunctionMemorySlots = &m_memorySlots.at(_functionDefinition.name);
|
|
||||||
for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables)
|
for (TypedName const& param: _functionDefinition.parameters + _functionDefinition.returnVariables)
|
||||||
if (m_currentFunctionMemorySlots->count(param.name))
|
if (m_memoryOffsetTracker(param.name))
|
||||||
{
|
{
|
||||||
// TODO: we cannot handle function parameters yet.
|
// TODO: we cannot handle function parameters yet.
|
||||||
m_currentFunctionMemorySlots = saved;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ASTModifier::operator()(_functionDefinition);
|
ASTModifier::operator()(_functionDefinition);
|
||||||
m_currentFunctionMemorySlots = saved;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StackToMemoryMover::operator()(Block& _block)
|
void StackToMemoryMover::operator()(Block& _block)
|
||||||
{
|
{
|
||||||
using OptionalStatements = std::optional<vector<Statement>>;
|
using OptionalStatements = std::optional<vector<Statement>>;
|
||||||
if (!m_currentFunctionMemorySlots)
|
|
||||||
{
|
|
||||||
ASTModifier::operator()(_block);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto containsVariableNeedingEscalation = [&](auto const& _variables) {
|
auto containsVariableNeedingEscalation = [&](auto const& _variables) {
|
||||||
return util::contains_if(_variables, [&](auto const& var) {
|
return util::contains_if(_variables, [&](auto const& var) {
|
||||||
return m_currentFunctionMemorySlots->count(var.name);
|
return m_memoryOffsetTracker(var.name);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
auto rewriteAssignmentOrVariableDeclaration = [&](
|
auto rewriteAssignmentOrVariableDeclaration = [&](
|
||||||
@ -123,12 +104,16 @@ void StackToMemoryMover::operator()(Block& _block)
|
|||||||
std::unique_ptr<Expression> _value
|
std::unique_ptr<Expression> _value
|
||||||
) -> std::vector<Statement> {
|
) -> std::vector<Statement> {
|
||||||
if (_variables.size() == 1)
|
if (_variables.size() == 1)
|
||||||
|
{
|
||||||
|
optional<YulString> offset = m_memoryOffsetTracker(_variables.front().name);
|
||||||
|
yulAssert(offset, "");
|
||||||
return generateMemoryStore(
|
return generateMemoryStore(
|
||||||
m_context.dialect,
|
m_context.dialect,
|
||||||
_loc,
|
_loc,
|
||||||
memoryOffset(_variables.front().name),
|
*offset,
|
||||||
_value ? *std::move(_value) : Literal{_loc, LiteralKind::Number, "0"_yulstring, {}}
|
_value ? *std::move(_value) : Literal{_loc, LiteralKind::Number, "0"_yulstring, {}}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
VariableDeclaration tempDecl{_loc, {}, std::move(_value)};
|
VariableDeclaration tempDecl{_loc, {}, std::move(_value)};
|
||||||
vector<Statement> memoryAssignments;
|
vector<Statement> memoryAssignments;
|
||||||
@ -138,11 +123,11 @@ void StackToMemoryMover::operator()(Block& _block)
|
|||||||
YulString tempVarName = m_nameDispenser.newName(var.name);
|
YulString tempVarName = m_nameDispenser.newName(var.name);
|
||||||
tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}});
|
tempDecl.variables.emplace_back(TypedName{var.location, tempVarName, {}});
|
||||||
|
|
||||||
if (m_currentFunctionMemorySlots->count(var.name))
|
if (optional<YulString> offset = m_memoryOffsetTracker(var.name))
|
||||||
memoryAssignments += generateMemoryStore(
|
memoryAssignments += generateMemoryStore(
|
||||||
m_context.dialect,
|
m_context.dialect,
|
||||||
_loc,
|
_loc,
|
||||||
memoryOffset(var.name),
|
*offset,
|
||||||
Identifier{_loc, tempVarName}
|
Identifier{_loc, tempVarName}
|
||||||
);
|
);
|
||||||
else if constexpr (std::is_same_v<std::decay_t<decltype(var)>, Identifier>)
|
else if constexpr (std::is_same_v<std::decay_t<decltype(var)>, Identifier>)
|
||||||
@ -201,10 +186,8 @@ void StackToMemoryMover::operator()(Block& _block)
|
|||||||
|
|
||||||
void StackToMemoryMover::visit(Expression& _expression)
|
void StackToMemoryMover::visit(Expression& _expression)
|
||||||
{
|
{
|
||||||
if (
|
if (Identifier* identifier = std::get_if<Identifier>(&_expression))
|
||||||
Identifier* identifier = std::get_if<Identifier>(&_expression);
|
if (optional<YulString> offset = m_memoryOffsetTracker(identifier->name))
|
||||||
identifier && m_currentFunctionMemorySlots && m_currentFunctionMemorySlots->count(identifier->name)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
BuiltinFunction const* memoryLoadFunction = m_context.dialect.memoryLoadFunction(m_context.dialect.defaultType);
|
BuiltinFunction const* memoryLoadFunction = m_context.dialect.memoryLoadFunction(m_context.dialect.defaultType);
|
||||||
yulAssert(memoryLoadFunction, "");
|
yulAssert(memoryLoadFunction, "");
|
||||||
@ -215,21 +198,24 @@ void StackToMemoryMover::visit(Expression& _expression)
|
|||||||
Literal{
|
Literal{
|
||||||
loc,
|
loc,
|
||||||
LiteralKind::Number,
|
LiteralKind::Number,
|
||||||
memoryOffset(identifier->name),
|
*offset,
|
||||||
{}
|
{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
ASTModifier::visit(_expression);
|
ASTModifier::visit(_expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
YulString StackToMemoryMover::memoryOffset(YulString _variable)
|
optional<YulString> StackToMemoryMover::VariableMemoryOffsetTracker::operator()(YulString _variable) const
|
||||||
{
|
{
|
||||||
yulAssert(m_currentFunctionMemorySlots, "");
|
if (m_memorySlots.count(_variable))
|
||||||
uint64_t slot = m_currentFunctionMemorySlots->at(_variable);
|
{
|
||||||
|
uint64_t slot = m_memorySlots.at(_variable);
|
||||||
yulAssert(slot < m_numRequiredSlots, "");
|
yulAssert(slot < m_numRequiredSlots, "");
|
||||||
return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))};
|
return YulString{util::toCompactHexWithPrefix(m_reservedMemory + 32 * (m_numRequiredSlots - slot - 1))};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ public:
|
|||||||
static void run(
|
static void run(
|
||||||
OptimiserStepContext& _context,
|
OptimiserStepContext& _context,
|
||||||
u256 _reservedMemory,
|
u256 _reservedMemory,
|
||||||
std::map<YulString, std::map<YulString, uint64_t>> const& _memorySlots,
|
std::map<YulString, uint64_t> const& _memorySlots,
|
||||||
uint64_t _numRequiredSlots,
|
uint64_t _numRequiredSlots,
|
||||||
Block& _block
|
Block& _block
|
||||||
);
|
);
|
||||||
@ -100,22 +100,33 @@ public:
|
|||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
void visit(Expression& _expression) override;
|
void visit(Expression& _expression) override;
|
||||||
private:
|
private:
|
||||||
|
class VariableMemoryOffsetTracker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VariableMemoryOffsetTracker(
|
||||||
|
u256 _reservedMemory,
|
||||||
|
std::map<YulString, uint64_t> const& _memorySlots,
|
||||||
|
uint64_t _numRequiredSlots
|
||||||
|
): m_reservedMemory(_reservedMemory), m_memorySlots(_memorySlots), m_numRequiredSlots(_numRequiredSlots)
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal
|
||||||
|
/// or std::nullopt if the variable should not be moved.
|
||||||
|
std::optional<YulString> operator()(YulString _variable) const;
|
||||||
|
private:
|
||||||
|
u256 m_reservedMemory;
|
||||||
|
std::map<YulString, uint64_t> const& m_memorySlots;
|
||||||
|
uint64_t m_numRequiredSlots = 0;
|
||||||
|
};
|
||||||
|
|
||||||
StackToMemoryMover(
|
StackToMemoryMover(
|
||||||
OptimiserStepContext& _context,
|
OptimiserStepContext& _context,
|
||||||
u256 _reservedMemory,
|
VariableMemoryOffsetTracker const& _memoryOffsetTracker
|
||||||
std::map<YulString, std::map<YulString, uint64_t>> const& _memorySlots,
|
|
||||||
uint64_t _numRequiredSlots
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @returns a YulString containing the memory offset to be assigned to @a _variable as number literal.
|
|
||||||
YulString memoryOffset(YulString _variable);
|
|
||||||
|
|
||||||
OptimiserStepContext& m_context;
|
OptimiserStepContext& m_context;
|
||||||
u256 m_reservedMemory;
|
VariableMemoryOffsetTracker const& m_memoryOffsetTracker;
|
||||||
std::map<YulString, std::map<YulString, uint64_t>> const& m_memorySlots;
|
|
||||||
uint64_t m_numRequiredSlots = 0;
|
|
||||||
NameDispenser& m_nameDispenser;
|
NameDispenser& m_nameDispenser;
|
||||||
std::map<YulString, uint64_t> const* m_currentFunctionMemorySlots = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
Error: Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
Error: Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
|
||||||
--> recovery_ast_empty_contract/input.sol:3:1:
|
--> recovery_ast_empty_contract/input.sol:3:1:
|
||||||
|
|
|
|
||||||
3 | c
|
3 | c
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Error (1834): Unimplemented feature error: setToZero for type t_struct$_S_$4_storage not yet implemented! in FILENAME REMOVED
|
Error (1834): Unimplemented feature error: Byte Arrays not yet implemented! in FILENAME REMOVED
|
||||||
--> yul_unimplemented/input.sol:9:9:
|
--> yul_unimplemented/input.sol:6:9:
|
||||||
|
|
|
|
||||||
9 | delete str;
|
6 | delete a;
|
||||||
| ^^^^^^^^^^
|
| ^^^^^^^^
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
// SPDX-License-Identifier: GPL-3.0
|
// SPDX-License-Identifier: GPL-3.0
|
||||||
pragma solidity >=0.0;
|
pragma solidity >=0.0;
|
||||||
contract test {
|
contract test {
|
||||||
struct S {
|
bytes a;
|
||||||
uint x;
|
function f() public {
|
||||||
}
|
delete a;
|
||||||
S str;
|
|
||||||
constructor() {
|
|
||||||
delete str;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,6 @@
|
|||||||
pragma solidity ^99.99.0;
|
pragma solidity ^99.99.0;
|
||||||
this is surely invalid
|
this is surely invalid
|
||||||
// ----
|
// ----
|
||||||
// ParserError 7858: (26-30): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
// ParserError 6635: (31-33): Expected identifier but got 'is'
|
||||||
|
// ParserError 6635: (34-40): Expected ';' but got identifier
|
||||||
|
// ParserError 6635: (49-49): Expected ';' but got end of source
|
||||||
|
@ -16,5 +16,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1
|
// test() -> 0x20, 0x8, -1, -1, 8, -16, -2, 6, 8, -1
|
||||||
|
@ -6,5 +6,7 @@ contract C {
|
|||||||
return (b[0], b.length);
|
return (b[0], b.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 1, 3
|
// f() -> 1, 3
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000
|
// test() -> 0x20, 29, 0x0303030303030303030303030303030303030303030303030303030303000000
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000
|
// test() -> 0x20, 33, 0x303030303030303030303030303030303030303030303030303030303030303, 0x0300000000000000000000000000000000000000000000000000000000000000
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
contract C {
|
||||||
|
uint72[5][] a;
|
||||||
|
|
||||||
|
function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) {
|
||||||
|
for (uint i = 0; i < 4; i++)
|
||||||
|
a.push();
|
||||||
|
a[0][0] = 1;
|
||||||
|
a[0][3] = 2;
|
||||||
|
a[1][1] = 3;
|
||||||
|
a[1][4] = 4;
|
||||||
|
a[2][0] = 5;
|
||||||
|
a[3][2] = 6;
|
||||||
|
a[3][3] = 7;
|
||||||
|
uint72[5][] memory m = a;
|
||||||
|
return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 1, 2, 3, 4, 5, 6, 7
|
@ -0,0 +1,13 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
contract C {
|
||||||
|
bytes[] a;
|
||||||
|
|
||||||
|
function f() public returns (bytes[] memory) {
|
||||||
|
a.push("abc");
|
||||||
|
a.push("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVXYZ");
|
||||||
|
bytes[] memory m = a;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 0x02, 0x40, 0x80, 3, 0x6162630000000000000000000000000000000000000000000000000000000000, 0x99, 44048183304486788312148433451363384677562265908331949128489393215789685032262, 32241931068525137014058842823026578386641954854143559838526554899205067598957, 49951309422467613961193228765530489307475214998374779756599339590522149884499, 0x54555658595a6162636465666768696a6b6c6d6e6f707172737475767778797a, 0x4142434445464748494a4b4c4d4e4f5051525354555658595a00000000000000
|
@ -0,0 +1,22 @@
|
|||||||
|
contract C {
|
||||||
|
uint72[5][] a;
|
||||||
|
|
||||||
|
function f() public returns (uint72, uint72, uint72, uint72, uint72, uint72, uint72) {
|
||||||
|
for (uint i = 0; i < 4; i++)
|
||||||
|
a.push();
|
||||||
|
a[0][0] = 1;
|
||||||
|
a[0][3] = 2;
|
||||||
|
a[1][1] = 3;
|
||||||
|
a[1][4] = 4;
|
||||||
|
a[2][0] = 5;
|
||||||
|
a[3][2] = 6;
|
||||||
|
a[3][3] = 7;
|
||||||
|
uint72[5][] storage a_ = a;
|
||||||
|
uint72[5][] memory m = a_;
|
||||||
|
return (m[0][0], m[0][3], m[1][1], m[1][4], m[2][0], m[3][2], m[3][3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 1, 2, 3, 4, 5, 6, 7
|
@ -0,0 +1,26 @@
|
|||||||
|
contract C {
|
||||||
|
struct T { uint8 x; uint8 y; uint[] z; }
|
||||||
|
T[3][] a;
|
||||||
|
|
||||||
|
function f() public returns (uint8, uint8, uint, uint, uint, uint8, uint8, uint, uint, uint) {
|
||||||
|
a.push();
|
||||||
|
a.push();
|
||||||
|
a[0][1].x = 11;
|
||||||
|
a[0][1].y = 12;
|
||||||
|
a[0][1].z.push(1);
|
||||||
|
a[0][1].z.push(2);
|
||||||
|
a[0][1].z.push(3);
|
||||||
|
a[1][2].x = 21;
|
||||||
|
a[1][2].y = 22;
|
||||||
|
a[1][2].z.push(4);
|
||||||
|
a[1][2].z.push(5);
|
||||||
|
a[1][2].z.push(6);
|
||||||
|
T[3][] memory m = a;
|
||||||
|
return (
|
||||||
|
m[0][1].x, m[0][1].y, m[0][1].z[0], m[0][1].z[1], m[0][1].z[2],
|
||||||
|
m[1][2].x, m[1][2].y, m[1][2].z[0], m[1][2].z[1], m[1][2].z[2]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() -> 11, 0x0c, 1, 2, 3, 0x15, 22, 4, 5, 6
|
@ -0,0 +1,15 @@
|
|||||||
|
contract C {
|
||||||
|
uint8[33] a;
|
||||||
|
|
||||||
|
function f() public returns (uint8, uint8, uint8) {
|
||||||
|
a[0] = 2;
|
||||||
|
a[16] = 3;
|
||||||
|
a[32] = 4;
|
||||||
|
uint8[33] memory m = a;
|
||||||
|
return (m[0], m[16], m[32]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 2, 3, 4
|
@ -0,0 +1,17 @@
|
|||||||
|
contract C {
|
||||||
|
uint8[] a;
|
||||||
|
|
||||||
|
function f() public returns (uint8, uint8, uint8) {
|
||||||
|
for (uint i = 0; i < 33; i++)
|
||||||
|
a.push(7);
|
||||||
|
a[0] = 2;
|
||||||
|
a[16] = 3;
|
||||||
|
a[32] = 4;
|
||||||
|
uint8[] memory m = a;
|
||||||
|
return (m[0], m[16], m[32]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 2, 3, 4
|
@ -16,5 +16,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access #
|
// f() -> true # This code interprets x as an array length and thus will go out of gas. neither of the two should throw due to out-of-bounds access #
|
||||||
|
@ -15,5 +15,7 @@ contract Test {
|
|||||||
return set(data)[1];
|
return set(data)[1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07
|
// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07
|
||||||
|
@ -31,6 +31,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// l() -> 0
|
// l() -> 0
|
||||||
// f(uint256): 42 ->
|
// f(uint256): 42 ->
|
||||||
|
@ -8,5 +8,7 @@ contract c {
|
|||||||
return keccak256(abi.encodePacked("b", keccak256(data), "a"));
|
return keccak256(abi.encodePacked("b", keccak256(data), "a"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc
|
// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc
|
||||||
|
@ -9,5 +9,7 @@ contract c {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// foo() -> true
|
// foo() -> true
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
address constant e = 0x1212121212121212121212121000002134593163;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public returns (address z) {
|
||||||
|
assembly { z := e }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// f() -> 0x1212121212121212121212121000002134593163
|
@ -0,0 +1,33 @@
|
|||||||
|
bytes constant a = "\x03\x01\x02";
|
||||||
|
bytes constant b = hex"030102";
|
||||||
|
string constant c = "hello";
|
||||||
|
uint256 constant x = 56;
|
||||||
|
enum ActionChoices {GoLeft, GoRight, GoStraight, Sit}
|
||||||
|
ActionChoices constant choices = ActionChoices.GoRight;
|
||||||
|
bytes32 constant st = "abc\x00\xff__";
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public returns (bytes memory) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public returns (bytes memory) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public returns (bytes memory) {
|
||||||
|
return bytes(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
function i() public returns (uint, ActionChoices, bytes32) {
|
||||||
|
return (x, choices, st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 3, "\x03\x01\x02"
|
||||||
|
// g() -> 0x20, 3, "\x03\x01\x02"
|
||||||
|
// h() -> 0x20, 5, "hello"
|
||||||
|
// i() -> 0x38, 1, 0x61626300ff5f5f00000000000000000000000000000000000000000000000000
|
@ -0,0 +1,41 @@
|
|||||||
|
==== Source: s1.sol ====
|
||||||
|
|
||||||
|
|
||||||
|
bytes constant a = b;
|
||||||
|
bytes constant b = hex"030102";
|
||||||
|
|
||||||
|
function fre() pure returns (bytes memory) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
==== Source: s2.sol ====
|
||||||
|
|
||||||
|
import "s1.sol";
|
||||||
|
|
||||||
|
uint256 constant c = uint8(a[0]) + 2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public returns (bytes memory) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public returns (bytes memory) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
function h() public returns (uint) {
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
function i() public returns (bytes memory) {
|
||||||
|
return fre();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x20, 3, "\x03\x01\x02"
|
||||||
|
// g() -> 0x20, 3, "\x03\x01\x02"
|
||||||
|
// h() -> 5
|
||||||
|
// i() -> 0x20, 3, "\x03\x01\x02"
|
@ -0,0 +1,26 @@
|
|||||||
|
==== Source: s1.sol ====
|
||||||
|
|
||||||
|
|
||||||
|
uint constant a = 89;
|
||||||
|
|
||||||
|
function fre() pure returns (uint) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
==== Source: s2.sol ====
|
||||||
|
|
||||||
|
import {a as b, fre} from "s1.sol";
|
||||||
|
import "s1.sol" as M;
|
||||||
|
|
||||||
|
uint256 constant a = 13;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
function f() public returns (uint, uint, uint, uint) {
|
||||||
|
return (a, fre(), M.a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0x0d, 0x59, 0x59, 0x59
|
@ -23,6 +23,8 @@ contract test {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f(bool): true -> 1
|
// f(bool): true -> 1
|
||||||
// f(bool): false -> 2
|
// f(bool): false -> 2
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
==== Source: a.sol ====
|
||||||
|
function f(uint) pure returns (uint) { return 7; }
|
||||||
|
function f(bytes memory x) pure returns (uint) { return x.length; }
|
||||||
|
==== Source: b.sol ====
|
||||||
|
import "a.sol" as M;
|
||||||
|
contract C {
|
||||||
|
function f() public pure returns (uint, uint) {
|
||||||
|
return (M.f(2), M.f("abc"));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 7, 3
|
@ -18,6 +18,8 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
// a() -> 7
|
// a() -> 7
|
||||||
|
@ -21,5 +21,7 @@ contract C {
|
|||||||
return (s.f(), h(s));
|
return (s.f(), h(s));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// g() -> 7, 7
|
// g() -> 7, 7
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint16 v;
|
||||||
|
S[] x;
|
||||||
|
}
|
||||||
|
uint8[77] padding;
|
||||||
|
S s;
|
||||||
|
constructor() {
|
||||||
|
s.v = 21;
|
||||||
|
s.x.push(); s.x.push(); s.x.push();
|
||||||
|
s.x[0].v = 101; s.x[1].v = 102; s.x[2].v = 103;
|
||||||
|
}
|
||||||
|
function f() public returns (uint256 a, uint256 b, uint256 c, uint256 d) {
|
||||||
|
S storage sptr1 = s.x[0];
|
||||||
|
S storage sptr2 = s.x[1];
|
||||||
|
S storage sptr3 = s.x[2];
|
||||||
|
uint256 slot1; uint256 slot2; uint256 slot3;
|
||||||
|
assembly { slot1 := sptr1.slot slot2 := sptr2.slot slot3 := sptr3.slot }
|
||||||
|
delete s;
|
||||||
|
assembly { a := sload(s.slot) b := sload(slot1) c := sload(slot2) d := sload(slot3) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() -> 0, 0, 0, 0
|
@ -15,5 +15,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> 1
|
// f() -> 1
|
||||||
|
@ -16,5 +16,7 @@ contract test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// deleteMember() -> 0
|
// deleteMember() -> 0
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint256 x;
|
||||||
|
uint128 y;
|
||||||
|
uint32 z;
|
||||||
|
}
|
||||||
|
uint8 b = 23;
|
||||||
|
S s;
|
||||||
|
uint8 a = 17;
|
||||||
|
function f() public {
|
||||||
|
s.x = 42; s.y = 42; s.y = 42;
|
||||||
|
delete s;
|
||||||
|
assert(s.x == 0);
|
||||||
|
assert(s.y == 0);
|
||||||
|
assert(s.z == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() ->
|
@ -0,0 +1,47 @@
|
|||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
contract C {
|
||||||
|
struct S {
|
||||||
|
uint128 a;
|
||||||
|
uint256[] x;
|
||||||
|
uint240 b;
|
||||||
|
}
|
||||||
|
uint8 b = 23;
|
||||||
|
S s;
|
||||||
|
uint8 a = 17;
|
||||||
|
function f() public {
|
||||||
|
delete s;
|
||||||
|
s.x.push(42); s.x.push(42); s.x.push(42);
|
||||||
|
delete s;
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
uint256[] storage x = s.x;
|
||||||
|
assembly { sstore(x.slot, 3) }
|
||||||
|
assert(s.x[0] == 0);
|
||||||
|
assert(s.x[1] == 0);
|
||||||
|
assert(s.x[2] == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
}
|
||||||
|
|
||||||
|
function g() public {
|
||||||
|
delete s;
|
||||||
|
s.x.push(42); s.x.push(42); s.x.push(42);
|
||||||
|
s.a = 1; s.b = 2;
|
||||||
|
delete s.x;
|
||||||
|
assert(s.x.length == 0);
|
||||||
|
uint256[] storage x = s.x;
|
||||||
|
assembly { sstore(x.slot, 3) }
|
||||||
|
assert(s.x[0] == 0);
|
||||||
|
assert(s.x[1] == 0);
|
||||||
|
assert(s.x[2] == 0);
|
||||||
|
assert(b == 23);
|
||||||
|
assert(a == 17);
|
||||||
|
assert(s.a == 1);
|
||||||
|
assert(s.b == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
|
// ----
|
||||||
|
// f() ->
|
||||||
|
// g() ->
|
@ -14,5 +14,7 @@ contract test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// deleteIt() -> 0
|
// deleteIt() -> 0
|
||||||
|
@ -18,6 +18,8 @@ contract test {
|
|||||||
inner.recursive[0].z = inner.recursive[1].z + 1;
|
inner.recursive[0].z = inner.recursive[1].z + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// check() -> false
|
// check() -> false
|
||||||
// set() ->
|
// set() ->
|
||||||
|
@ -22,5 +22,7 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ====
|
||||||
|
// compileViaYul: also
|
||||||
// ----
|
// ----
|
||||||
// f() -> true
|
// f() -> true
|
||||||
|
@ -4,6 +4,6 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables.
|
// DeclarationError 1788: (28-45): The "constant" keyword can only be used for state variables or variables at file level.
|
||||||
// TypeError 5462: (69-72): Invalid array length, expected integer literal or constant expression.
|
// TypeError 5462: (69-72): Invalid array length, expected integer literal or constant expression.
|
||||||
// TypeError 6651: (64-75): Data location must be "storage", "memory" or "calldata" for variable, but none was given.
|
// TypeError 6651: (64-75): Data location must be "storage", "memory" or "calldata" for variable, but none was given.
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
/// @dev Documentation
|
||||||
|
uint constant x = 8;
|
||||||
|
// ----
|
@ -0,0 +1,4 @@
|
|||||||
|
/// Documentation
|
||||||
|
uint constant x = 8;
|
||||||
|
// ----
|
||||||
|
// DocstringParsingError 6546: (0-18): Documentation tag @notice not valid for file-level variables.
|
@ -0,0 +1,3 @@
|
|||||||
|
uint constant override x = 2;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (14-22): Expected identifier but got 'override'
|
@ -0,0 +1,3 @@
|
|||||||
|
uint constant x;
|
||||||
|
// ----
|
||||||
|
// TypeError 4266: (0-15): Uninitialized "constant" variable.
|
@ -0,0 +1,3 @@
|
|||||||
|
uint constant virtual x;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (14-21): Expected identifier but got 'virtual'
|
@ -0,0 +1,3 @@
|
|||||||
|
uint public constant x = 7;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (5-11): Expected identifier but got 'public'
|
@ -0,0 +1,3 @@
|
|||||||
|
uint constant public y = 7;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (14-20): Expected identifier but got 'public'
|
15
test/libsolidity/syntaxTests/constants/cross_file_cyclic.sol
Normal file
15
test/libsolidity/syntaxTests/constants/cross_file_cyclic.sol
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
import "b";
|
||||||
|
uint constant c = d;
|
||||||
|
==== Source: b ====
|
||||||
|
import "a";
|
||||||
|
uint constant b = c;
|
||||||
|
uint constant d = b;
|
||||||
|
contract C {
|
||||||
|
uint constant a = b;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6161: (b:12-31): The value of the constant b has a cyclic dependency via c.
|
||||||
|
// TypeError 6161: (b:33-52): The value of the constant d has a cyclic dependency via b.
|
||||||
|
// TypeError 6161: (b:71-90): The value of the constant a has a cyclic dependency via b.
|
||||||
|
// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d.
|
@ -0,0 +1,15 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
import "b";
|
||||||
|
uint constant c = d;
|
||||||
|
==== Source: b ====
|
||||||
|
import "a" as M;
|
||||||
|
uint constant b = M.c;
|
||||||
|
uint constant d = b;
|
||||||
|
contract C {
|
||||||
|
uint constant a = b;
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6161: (b:17-38): The value of the constant b has a cyclic dependency via c.
|
||||||
|
// TypeError 6161: (b:40-59): The value of the constant d has a cyclic dependency via b.
|
||||||
|
// TypeError 6161: (b:78-97): The value of the constant a has a cyclic dependency via b.
|
||||||
|
// TypeError 6161: (a:12-31): The value of the constant c has a cyclic dependency via d.
|
@ -0,0 +1,3 @@
|
|||||||
|
uint[] memory constant x = 2;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (7-13): Expected identifier but got 'memory'
|
@ -0,0 +1,3 @@
|
|||||||
|
uint[] constant memory x = 2;
|
||||||
|
// ----
|
||||||
|
// ParserError 2314: (16-22): Expected identifier but got 'memory'
|
@ -0,0 +1,4 @@
|
|||||||
|
uint immutable x = 7;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 8342: (0-20): Only constant variables are allowed at file level.
|
||||||
|
// DeclarationError 8297: (0-20): The "immutable" keyword can only be used for state variables.
|
@ -0,0 +1,3 @@
|
|||||||
|
mapping(uint => uint) constant b = b;
|
||||||
|
// ----
|
||||||
|
// TypeError 9259: (0-36): Constants of non-value type not yet implemented.
|
@ -0,0 +1,7 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
uint constant c = 7;
|
||||||
|
==== Source: b ====
|
||||||
|
import {c as d} from "a";
|
||||||
|
uint constant d = 7;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2333: (b:26-45): Identifier already declared.
|
3
test/libsolidity/syntaxTests/constants/non_constant.sol
Normal file
3
test/libsolidity/syntaxTests/constants/non_constant.sol
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
uint x = 7;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 8342: (0-10): Only constant variables are allowed at file level.
|
@ -0,0 +1,8 @@
|
|||||||
|
==== Source: a ====
|
||||||
|
import "b";
|
||||||
|
uint constant c = 7;
|
||||||
|
==== Source: b ====
|
||||||
|
import "a";
|
||||||
|
uint constant c = 7;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2333: (b:12-31): Identifier already declared.
|
@ -0,0 +1,4 @@
|
|||||||
|
uint constant c = 7;
|
||||||
|
uint constant c = 8;
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2333: (21-40): Identifier already declared.
|
@ -0,0 +1,5 @@
|
|||||||
|
struct S { uint x; }
|
||||||
|
S constant s;
|
||||||
|
// ----
|
||||||
|
// TypeError 9259: (21-33): Constants of non-value type not yet implemented.
|
||||||
|
// TypeError 4266: (21-33): Uninitialized "constant" variable.
|
@ -1,3 +1,3 @@
|
|||||||
constructor() {}
|
constructor() {}
|
||||||
// ----
|
// ----
|
||||||
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
// ParserError 7858: (0-11): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
fallback(){}
|
fallback(){}
|
||||||
// ----
|
// ----
|
||||||
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
// ParserError 7858: (0-8): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
receive() {}
|
receive() {}
|
||||||
// ----
|
// ----
|
||||||
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
// ParserError 7858: (0-7): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
|
||||||
|
@ -2,4 +2,4 @@ contract test {
|
|||||||
function f(uint[] memory constant a) public { }
|
function f(uint[] memory constant a) public { }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables.
|
// DeclarationError 1788: (31-55): The "constant" keyword can only be used for state variables or variables at file level.
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
uint constant c = 7;
|
||||||
|
function c() returns (uint) {}
|
||||||
|
// ----
|
||||||
|
// DeclarationError 2333: (21-51): Identifier already declared.
|
@ -11,4 +11,4 @@ contract C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// ParserError 2837: (290-295): Only state variables can have a docstring.
|
// ParserError 2837: (290-295): Only state variables or file-level variables can have a docstring.
|
||||||
|
@ -2,4 +2,4 @@ contract Foo {
|
|||||||
function f(uint[] storage constant x, uint[] memory y) internal { }
|
function f(uint[] storage constant x, uint[] memory y) internal { }
|
||||||
}
|
}
|
||||||
// ----
|
// ----
|
||||||
// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables.
|
// DeclarationError 1788: (30-55): The "constant" keyword can only be used for state variables or variables at file level.
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
struct S { mapping(uint => uint)[2] a; }
|
||||||
|
contract C {
|
||||||
|
function f(S storage s) public {}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// TypeError 6651: (69-80): Data location must be "memory" or "calldata" for parameter in function, but "storage" was given.
|
@ -0,0 +1,4 @@
|
|||||||
|
struct S { mapping(uint => uint)[2] a; }
|
||||||
|
library L {
|
||||||
|
function f(S storage s) public {}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
library Test {
|
||||||
|
struct Nested { mapping(uint => uint)[2][] a; }
|
||||||
|
struct X { Nested n; }
|
||||||
|
function f(X storage x) public {}
|
||||||
|
}
|
@ -1,3 +1,3 @@
|
|||||||
unexpected
|
unexpected
|
||||||
// ----
|
// ----
|
||||||
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/function definition.
|
// ParserError 7858: (0-10): Expected pragma, import directive or contract/interface/library/struct/enum/constant/function definition.
|
||||||
|
Loading…
Reference in New Issue
Block a user