mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' of github.com:tfire/solidity into fix/remove-namespace-ast-annotations
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
#include <liblangutil/Common.h>
|
||||
|
||||
#include <range/v3/algorithm/any_of.hpp>
|
||||
#include <range/v3/view/filter.hpp>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
@@ -162,6 +163,81 @@ bool DocStringTagParser::visit(ErrorDefinition const& _error)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DocStringTagParser::visit(InlineAssembly const& _assembly)
|
||||
{
|
||||
if (!_assembly.documentation())
|
||||
return true;
|
||||
StructuredDocumentation documentation{-1, _assembly.location(), _assembly.documentation()};
|
||||
ErrorList errors;
|
||||
ErrorReporter errorReporter{errors};
|
||||
auto docTags = DocStringParser{documentation, errorReporter}.parse();
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
SecondarySourceLocation ssl;
|
||||
for (auto const& error: errors)
|
||||
if (error->comment())
|
||||
ssl.append(
|
||||
*error->comment(),
|
||||
_assembly.location()
|
||||
);
|
||||
m_errorReporter.warning(
|
||||
7828_error,
|
||||
_assembly.location(),
|
||||
"Inline assembly has invalid NatSpec documentation.",
|
||||
ssl
|
||||
);
|
||||
}
|
||||
|
||||
for (auto const& [tagName, tagValue]: docTags)
|
||||
{
|
||||
if (tagName == "solidity")
|
||||
{
|
||||
vector<string> values;
|
||||
boost::split(values, tagValue.content, isWhiteSpace);
|
||||
|
||||
set<string> valuesSeen;
|
||||
set<string> duplicates;
|
||||
for (auto const& value: values | ranges::views::filter(not_fn(&string::empty)))
|
||||
if (valuesSeen.insert(value).second)
|
||||
{
|
||||
if (value == "memory-safe-assembly")
|
||||
{
|
||||
if (_assembly.annotation().markedMemorySafe)
|
||||
m_errorReporter.warning(
|
||||
8544_error,
|
||||
_assembly.location(),
|
||||
"Inline assembly marked as memory safe using both a NatSpec tag and an assembly flag. "
|
||||
"If you are not concerned with backwards compatibility, only use the assembly flag, "
|
||||
"otherwise only use the NatSpec tag."
|
||||
);
|
||||
_assembly.annotation().markedMemorySafe = true;
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
8787_error,
|
||||
_assembly.location(),
|
||||
"Unexpected value for @solidity tag in inline assembly: " + value
|
||||
);
|
||||
}
|
||||
else if (duplicates.insert(value).second)
|
||||
m_errorReporter.warning(
|
||||
4377_error,
|
||||
_assembly.location(),
|
||||
"Value for @solidity tag in inline assembly specified multiple times: " + value
|
||||
);
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
6269_error,
|
||||
_assembly.location(),
|
||||
"Unexpected NatSpec tag \"" + tagName + "\" with value \"" + tagValue.content + "\" in inline assembly."
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DocStringTagParser::checkParameters(
|
||||
CallableDeclaration const& _callable,
|
||||
StructurallyDocumented const& _node,
|
||||
|
||||
@@ -48,6 +48,7 @@ private:
|
||||
bool visit(ModifierDefinition const& _modifier) override;
|
||||
bool visit(EventDefinition const& _event) override;
|
||||
bool visit(ErrorDefinition const& _error) override;
|
||||
bool visit(InlineAssembly const& _assembly) override;
|
||||
|
||||
void checkParameters(
|
||||
CallableDeclaration const& _callable,
|
||||
|
||||
@@ -334,6 +334,27 @@ bool SyntaxChecker::visit(UnaryOperation const& _operation)
|
||||
|
||||
bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
if (_inlineAssembly.flags())
|
||||
for (auto flag: *_inlineAssembly.flags())
|
||||
{
|
||||
if (*flag == "memory-safe")
|
||||
{
|
||||
if (_inlineAssembly.annotation().markedMemorySafe)
|
||||
m_errorReporter.syntaxError(
|
||||
7026_error,
|
||||
_inlineAssembly.location(),
|
||||
"Inline assembly marked memory-safe multiple times."
|
||||
);
|
||||
_inlineAssembly.annotation().markedMemorySafe = true;
|
||||
}
|
||||
else
|
||||
m_errorReporter.warning(
|
||||
4430_error,
|
||||
_inlineAssembly.location(),
|
||||
"Unknown inline assembly flag: \"" + *flag + "\""
|
||||
);
|
||||
}
|
||||
|
||||
if (!m_useYulOptimizer)
|
||||
return false;
|
||||
|
||||
|
||||
@@ -763,6 +763,7 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
|
||||
|
||||
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
{
|
||||
bool lvalueAccessToMemoryVariable = false;
|
||||
// External references have already been resolved in a prior stage and stored in the annotation.
|
||||
// We run the resolve step again regardless.
|
||||
yul::ExternalIdentifierAccess::Resolver identifierAccess = [&](
|
||||
@@ -787,6 +788,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
if (auto var = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
{
|
||||
solAssert(var->type(), "Expected variable type!");
|
||||
if (_context == yul::IdentifierContext::LValue && var->type()->dataStoredIn(DataLocation::Memory))
|
||||
lvalueAccessToMemoryVariable = true;
|
||||
if (var->immutable())
|
||||
{
|
||||
m_errorReporter.typeError(3773_error, nativeLocationOf(_identifier), "Assembly access to immutable variables is not supported.");
|
||||
@@ -974,8 +977,11 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
|
||||
identifierAccess
|
||||
);
|
||||
if (!analyzer.analyze(_inlineAssembly.operations()))
|
||||
return false;
|
||||
return true;
|
||||
solAssert(m_errorReporter.hasErrors());
|
||||
_inlineAssembly.annotation().hasMemoryEffects =
|
||||
lvalueAccessToMemoryVariable ||
|
||||
(analyzer.sideEffects().memory != yul::SideEffects::None);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TypeChecker::visit(IfStatement const& _ifStatement)
|
||||
|
||||
@@ -1463,19 +1463,26 @@ public:
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _docString,
|
||||
yul::Dialect const& _dialect,
|
||||
ASTPointer<std::vector<ASTPointer<ASTString>>> _flags,
|
||||
std::shared_ptr<yul::Block> _operations
|
||||
):
|
||||
Statement(_id, _location, _docString), m_dialect(_dialect), m_operations(std::move(_operations)) {}
|
||||
Statement(_id, _location, _docString),
|
||||
m_dialect(_dialect),
|
||||
m_flags(move(_flags)),
|
||||
m_operations(std::move(_operations))
|
||||
{}
|
||||
void accept(ASTVisitor& _visitor) override;
|
||||
void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
yul::Dialect const& dialect() const { return m_dialect; }
|
||||
yul::Block const& operations() const { return *m_operations; }
|
||||
ASTPointer<std::vector<ASTPointer<ASTString>>> const& flags() const { return m_flags; }
|
||||
|
||||
InlineAssemblyAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
yul::Dialect const& m_dialect;
|
||||
ASTPointer<std::vector<ASTPointer<ASTString>>> m_flags;
|
||||
std::shared_ptr<yul::Block> m_operations;
|
||||
};
|
||||
|
||||
|
||||
@@ -219,6 +219,10 @@ struct InlineAssemblyAnnotation: StatementAnnotation
|
||||
std::map<yul::Identifier const*, ExternalIdentifierInfo> externalReferences;
|
||||
/// Information generated during analysis phase.
|
||||
std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo;
|
||||
/// True, if the assembly block was annotated to be memory-safe.
|
||||
bool markedMemorySafe = false;
|
||||
/// True, if the assembly block involves any memory opcode or assigns to variables in memory.
|
||||
SetOnce<bool> hasMemoryEffects;
|
||||
};
|
||||
|
||||
struct BlockAnnotation: StatementAnnotation, ScopableAnnotation
|
||||
|
||||
@@ -600,11 +600,23 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
|
||||
for (Json::Value& it: externalReferences | ranges::views::values)
|
||||
externalReferencesJson.append(std::move(it));
|
||||
|
||||
setJsonNode(_node, "InlineAssembly", {
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))),
|
||||
make_pair("externalReferences", std::move(externalReferencesJson)),
|
||||
make_pair("evmVersion", dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect()).evmVersion().name())
|
||||
});
|
||||
};
|
||||
|
||||
if (_node.flags())
|
||||
{
|
||||
Json::Value flags(Json::arrayValue);
|
||||
for (auto const& flag: *_node.flags())
|
||||
if (flag)
|
||||
flags.append(*flag);
|
||||
else
|
||||
flags.append(Json::nullValue);
|
||||
attributes.emplace_back(make_pair("flags", move(flags)));
|
||||
}
|
||||
setJsonNode(_node, "InlineAssembly", move(attributes));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -626,11 +626,24 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
|
||||
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
|
||||
|
||||
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value());
|
||||
ASTPointer<vector<ASTPointer<ASTString>>> flags;
|
||||
if (_node.isMember("flags"))
|
||||
{
|
||||
flags = make_shared<vector<ASTPointer<ASTString>>>();
|
||||
Json::Value const& flagsNode = _node["flags"];
|
||||
astAssert(flagsNode.isArray(), "Assembly flags must be an array.");
|
||||
for (Json::ArrayIndex i = 0; i < flagsNode.size(); ++i)
|
||||
{
|
||||
astAssert(flagsNode[i].isString(), "Assembly flag must be a string.");
|
||||
flags->emplace_back(make_shared<ASTString>(flagsNode[i].asString()));
|
||||
}
|
||||
}
|
||||
shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST")));
|
||||
return createASTNode<InlineAssembly>(
|
||||
_node,
|
||||
nullOrASTString(_node, "documentation"),
|
||||
dialect,
|
||||
move(flags),
|
||||
operations
|
||||
);
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <libsolutil/CommonIO.h>
|
||||
#include <libsolutil/FunctionSelector.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <libsolutil/UTF8.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -781,8 +782,8 @@ tuple<bool, rational> RationalNumberType::parseRational(string const& _value)
|
||||
if (radixPoint != _value.end())
|
||||
{
|
||||
if (
|
||||
!all_of(radixPoint + 1, _value.end(), ::isdigit) ||
|
||||
!all_of(_value.begin(), radixPoint, ::isdigit)
|
||||
!all_of(radixPoint + 1, _value.end(), util::isDigit) ||
|
||||
!all_of(_value.begin(), radixPoint, util::isDigit)
|
||||
)
|
||||
return make_tuple(false, rational(0));
|
||||
|
||||
|
||||
@@ -572,8 +572,7 @@ void CompilerContext::updateSourceLocation()
|
||||
evmasm::Assembly::OptimiserSettings CompilerContext::translateOptimiserSettings(OptimiserSettings const& _settings)
|
||||
{
|
||||
// Constructing it this way so that we notice changes in the fields.
|
||||
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, false, m_evmVersion, 0};
|
||||
asmSettings.isCreation = true;
|
||||
evmasm::Assembly::OptimiserSettings asmSettings{false, false, false, false, false, false, m_evmVersion, 0};
|
||||
asmSettings.runInliner = _settings.runInliner;
|
||||
asmSettings.runJumpdestRemover = _settings.runJumpdestRemover;
|
||||
asmSettings.runPeephole = _settings.runPeephole;
|
||||
|
||||
@@ -65,7 +65,7 @@ public:
|
||||
RevertStrings _revertStrings,
|
||||
CompilerContext* _runtimeContext = nullptr
|
||||
):
|
||||
m_asm(std::make_shared<evmasm::Assembly>()),
|
||||
m_asm(std::make_shared<evmasm::Assembly>(_runtimeContext != nullptr, std::string{})),
|
||||
m_evmVersion(_evmVersion),
|
||||
m_revertStrings(_revertStrings),
|
||||
m_reservedMemory{0},
|
||||
|
||||
@@ -160,8 +160,8 @@ public:
|
||||
|
||||
std::set<ContractDefinition const*, ASTNode::CompareByID>& subObjectsCreated() { return m_subObjects; }
|
||||
|
||||
bool inlineAssemblySeen() const { return m_inlineAssemblySeen; }
|
||||
void setInlineAssemblySeen() { m_inlineAssemblySeen = true; }
|
||||
bool memoryUnsafeInlineAssemblySeen() const { return m_memoryUnsafeInlineAssemblySeen; }
|
||||
void setMemoryUnsafeInlineAssemblySeen() { m_memoryUnsafeInlineAssemblySeen = true; }
|
||||
|
||||
/// @returns the runtime ID to be used for the function in the dispatch routine
|
||||
/// and for internal function pointers.
|
||||
@@ -202,8 +202,8 @@ private:
|
||||
/// Whether to use checked or wrapping arithmetic.
|
||||
Arithmetic m_arithmetic = Arithmetic::Checked;
|
||||
|
||||
/// Flag indicating whether any inline assembly block was seen.
|
||||
bool m_inlineAssemblySeen = false;
|
||||
/// Flag indicating whether any memory-unsafe inline assembly block was seen.
|
||||
bool m_memoryUnsafeInlineAssemblySeen = false;
|
||||
|
||||
/// Function definitions queued for code generation. They're the Solidity functions whose calls
|
||||
/// were discovered by the IR generator during AST traversal.
|
||||
|
||||
@@ -213,8 +213,8 @@ string IRGenerator::generate(
|
||||
t("subObjects", subObjectSources(m_context.subObjectsCreated()));
|
||||
|
||||
// This has to be called only after all other code generation for the creation object is complete.
|
||||
bool creationInvolvesAssembly = m_context.inlineAssemblySeen();
|
||||
t("memoryInitCreation", memoryInit(!creationInvolvesAssembly));
|
||||
bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
|
||||
t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly));
|
||||
t("useSrcMapCreation", formatUseSrcMap(m_context));
|
||||
|
||||
resetContext(_contract, ExecutionContext::Deployed);
|
||||
@@ -239,8 +239,8 @@ string IRGenerator::generate(
|
||||
t("useSrcMapDeployed", formatUseSrcMap(m_context));
|
||||
|
||||
// This has to be called only after all other code generation for the deployed object is complete.
|
||||
bool deployedInvolvesAssembly = m_context.inlineAssemblySeen();
|
||||
t("memoryInitDeployed", memoryInit(!deployedInvolvesAssembly));
|
||||
bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
|
||||
t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly));
|
||||
|
||||
solAssert(_contract.annotation().creationCallGraph->get() != nullptr, "");
|
||||
solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, "");
|
||||
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
else
|
||||
solAssert(false);
|
||||
|
||||
if (isdigit(value.front()))
|
||||
if (isDigit(value.front()))
|
||||
return yul::Literal{_identifier.debugData, yul::LiteralKind::Number, yul::YulString{value}, {}};
|
||||
else
|
||||
return yul::Identifier{_identifier.debugData, yul::YulString{value}};
|
||||
@@ -2138,7 +2138,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm)
|
||||
{
|
||||
setLocation(_inlineAsm);
|
||||
m_context.setInlineAssemblySeen();
|
||||
if (*_inlineAsm.annotation().hasMemoryEffects && !_inlineAsm.annotation().markedMemorySafe)
|
||||
m_context.setMemoryUnsafeInlineAssemblySeen();
|
||||
CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences};
|
||||
|
||||
yul::Statement modified = bodyCopier(_inlineAsm.operations());
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <libsmtutil/CHCSmtLib2Interface.h>
|
||||
#include <liblangutil/CharStreamProvider.h>
|
||||
#include <libsolutil/Algorithms.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
|
||||
#ifdef HAVE_Z3_DLOPEN
|
||||
#include <z3_version.h>
|
||||
@@ -1998,9 +1999,9 @@ map<unsigned, vector<unsigned>> CHC::summaryCalls(CHCSolverInterface::CexGraph c
|
||||
// Predicates that do not have a CALLID have a predicate id at the end of <suffix>,
|
||||
// so the assertion below should still hold.
|
||||
auto beg = _s.data();
|
||||
while (beg != _s.data() + _s.size() && !isdigit(*beg)) ++beg;
|
||||
while (beg != _s.data() + _s.size() && !isDigit(*beg)) ++beg;
|
||||
auto end = beg;
|
||||
while (end != _s.data() + _s.size() && isdigit(*end)) ++end;
|
||||
while (end != _s.data() + _s.size() && isDigit(*end)) ++end;
|
||||
|
||||
solAssert(beg != end, "Expected to find numerical call or predicate id.");
|
||||
|
||||
|
||||
@@ -1321,13 +1321,28 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
|
||||
advance();
|
||||
}
|
||||
|
||||
ASTPointer<vector<ASTPointer<ASTString>>> flags;
|
||||
if (m_scanner->currentToken() == Token::LParen)
|
||||
{
|
||||
flags = make_shared<vector<ASTPointer<ASTString>>>();
|
||||
do
|
||||
{
|
||||
advance();
|
||||
expectToken(Token::StringLiteral, false);
|
||||
flags->emplace_back(make_shared<ASTString>(m_scanner->currentLiteral()));
|
||||
advance();
|
||||
}
|
||||
while (m_scanner->currentToken() == Token::Comma);
|
||||
expectToken(Token::RParen);
|
||||
}
|
||||
|
||||
yul::Parser asmParser(m_errorReporter, dialect);
|
||||
shared_ptr<yul::Block> block = asmParser.parseInline(m_scanner);
|
||||
if (block == nullptr)
|
||||
BOOST_THROW_EXCEPTION(FatalError());
|
||||
|
||||
location.end = nativeLocationOf(*block).end;
|
||||
return make_shared<InlineAssembly>(nextID(), location, _docString, dialect, block);
|
||||
return make_shared<InlineAssembly>(nextID(), location, _docString, dialect, move(flags), block);
|
||||
}
|
||||
|
||||
ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)
|
||||
|
||||
Reference in New Issue
Block a user