Allow memory-safe inline assembly dialect flag.

This commit is contained in:
Daniel Kirchner 2022-02-14 13:21:15 +01:00
parent 6b6e163be5
commit 290b1c3a90
6 changed files with 82 additions and 4 deletions

View File

@ -202,7 +202,17 @@ bool DocStringTagParser::visit(InlineAssembly const& _assembly)
if (valuesSeen.insert(value).second) if (valuesSeen.insert(value).second)
{ {
if (value == "memory-safe-assembly") 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; _assembly.annotation().markedMemorySafe = true;
}
else else
m_errorReporter.warning( m_errorReporter.warning(
8787_error, 8787_error,

View File

@ -334,6 +334,27 @@ bool SyntaxChecker::visit(UnaryOperation const& _operation)
bool SyntaxChecker::visit(InlineAssembly const& _inlineAssembly) 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) if (!m_useYulOptimizer)
return false; return false;

View File

@ -1463,19 +1463,26 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
yul::Dialect const& _dialect, yul::Dialect const& _dialect,
ASTPointer<std::vector<ASTPointer<ASTString>>> _flags,
std::shared_ptr<yul::Block> _operations 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(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
yul::Dialect const& dialect() const { return m_dialect; } yul::Dialect const& dialect() const { return m_dialect; }
yul::Block const& operations() const { return *m_operations; } yul::Block const& operations() const { return *m_operations; }
ASTPointer<std::vector<ASTPointer<ASTString>>> const& flags() const { return m_flags; }
InlineAssemblyAnnotation& annotation() const override; InlineAssemblyAnnotation& annotation() const override;
private: private:
yul::Dialect const& m_dialect; yul::Dialect const& m_dialect;
ASTPointer<std::vector<ASTPointer<ASTString>>> m_flags;
std::shared_ptr<yul::Block> m_operations; std::shared_ptr<yul::Block> m_operations;
}; };

View File

@ -600,11 +600,23 @@ bool ASTJsonConverter::visit(InlineAssembly const& _node)
for (Json::Value& it: externalReferences | ranges::views::values) for (Json::Value& it: externalReferences | ranges::views::values)
externalReferencesJson.append(std::move(it)); 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("AST", Json::Value(yul::AsmJsonConverter(sourceIndexFromLocation(_node.location()))(_node.operations()))),
make_pair("externalReferences", std::move(externalReferencesJson)), make_pair("externalReferences", std::move(externalReferencesJson)),
make_pair("evmVersion", dynamic_cast<solidity::yul::EVMDialect const&>(_node.dialect()).evmVersion().name()) 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; return false;
} }

View File

@ -626,11 +626,24 @@ ASTPointer<InlineAssembly> ASTJsonImporter::createInlineAssembly(Json::Value con
astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!"); astAssert(m_evmVersion == evmVersion, "Imported tree evm version differs from configured evm version!");
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(evmVersion.value()); 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"))); shared_ptr<yul::Block> operations = make_shared<yul::Block>(yul::AsmJsonImporter(m_sourceNames).createBlock(member(_node, "AST")));
return createASTNode<InlineAssembly>( return createASTNode<InlineAssembly>(
_node, _node,
nullOrASTString(_node, "documentation"), nullOrASTString(_node, "documentation"),
dialect, dialect,
move(flags),
operations operations
); );
} }

View File

@ -1321,13 +1321,28 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
advance(); 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); yul::Parser asmParser(m_errorReporter, dialect);
shared_ptr<yul::Block> block = asmParser.parseInline(m_scanner); shared_ptr<yul::Block> block = asmParser.parseInline(m_scanner);
if (block == nullptr) if (block == nullptr)
BOOST_THROW_EXCEPTION(FatalError()); BOOST_THROW_EXCEPTION(FatalError());
location.end = nativeLocationOf(*block).end; 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) ASTPointer<IfStatement> Parser::parseIfStatement(ASTPointer<ASTString> const& _docString)