Merge pull request #7386 from ethereum/060-strict-inline-assembly

Defaulting to strict inline assembly (instead of loose)
This commit is contained in:
chriseth 2019-10-28 12:48:58 +01:00 committed by GitHub
commit 607bf24afe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 97 additions and 520 deletions

View File

@ -10,9 +10,9 @@ Breaking changes:
* General: New reserved keywords: ``virtual``.
* Standard JSON Interface: Add option to disable or choose hash method between IPFS and Swarm for the bytecode metadata.
* Syntax: ``push(element)`` for dynamic storage arrays do not return the new length anymore.
* Inline Assembly: Only strict inline assembly is allowed.
* Type checker: Resulting type of exponentiation is equal to the type of the base. Also allow signed types for the base.
Language Features:
* Allow global enums and structs.
* Allow underscores as delimiters in hex strings.

View File

@ -763,8 +763,6 @@ Grammar::
AssemblyExpression |
AssemblyLocalDefinition |
AssemblyAssignment |
AssemblyStackAssignment |
LabelDefinition |
AssemblyIf |
AssemblySwitch |
AssemblyFunctionDefinition |
@ -780,8 +778,6 @@ Grammar::
AssemblyAssignment = IdentifierOrList ':=' AssemblyExpression
IdentifierOrList = Identifier | '(' IdentifierList ')'
IdentifierList = Identifier ( ',' Identifier)*
AssemblyStackAssignment = '=:' Identifier
LabelDefinition = Identifier ':'
AssemblyIf = 'if' AssemblyExpression AssemblyBlock
AssemblySwitch = 'switch' AssemblyExpression AssemblyCase*
( 'default' AssemblyBlock )?

View File

@ -323,12 +323,10 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
// We use the latest EVM version because we will re-run it anyway.
yul::AsmAnalysisInfo analysisInfo;
boost::optional<Error::Type> errorTypeForLoose = Error::Type::SyntaxError;
yul::AsmAnalyzer(
analysisInfo,
errorsIgnored,
errorTypeForLoose,
yul::EVMDialect::looseAssemblyForEVM(EVMVersion{}),
yul::EVMDialect::strictAssemblyForEVM(EVMVersion{}),
resolver
).analyze(_inlineAssembly.operations());
return false;

View File

@ -735,7 +735,6 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
yul::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
Error::Type::SyntaxError,
_inlineAssembly.dialect(),
identifierAccess
);

View File

@ -41,7 +41,6 @@ public:
m_dialect(_dialect),
m_reportMutability(_reportMutability) {}
void operator()(yul::Label const&) { }
void operator()(yul::Instruction const& _instruction)
{
checkInstruction(_instruction.location, _instruction.instruction);
@ -58,7 +57,6 @@ public:
{
boost::apply_visitor(*this, _expr.expression);
}
void operator()(yul::StackAssignment const&) {}
void operator()(yul::Assignment const& _assignment)
{
boost::apply_visitor(*this, *_assignment.value);

View File

@ -411,7 +411,6 @@ void CompilerContext::appendInlineAssembly(
analyzerResult = yul::AsmAnalyzer(
analysisInfo,
errorReporter,
boost::none,
dialect,
identifierAccess.resolve
).analyze(*parserResult);

View File

@ -1152,7 +1152,7 @@ ASTPointer<InlineAssembly> Parser::parseInlineAssembly(ASTPointer<ASTString> con
SourceLocation location{position(), -1, source()};
expectToken(Token::Assembly);
yul::Dialect const& dialect = yul::EVMDialect::looseAssemblyForEVM(m_evmVersion);
yul::Dialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
if (m_scanner->currentToken() == Token::StringLiteral)
{
if (m_scanner->currentLiteral() != "evmasm")

View File

@ -78,7 +78,6 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
bool success = yul::AsmAnalyzer(
analysisInfo,
errors,
Error::Type::SyntaxError,
_dialect,
{},
_object.dataNames()
@ -87,24 +86,9 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect,
return analysisInfo;
}
bool AsmAnalyzer::operator()(Label const& _label)
{
solAssert(!_label.name.empty(), "");
checkLooseFeature(
_label.location,
"The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead."
);
m_info.stackHeightInfo[&_label] = m_stackHeight;
warnOnInstructions(dev::eth::Instruction::JUMPDEST, _label.location);
return true;
}
bool AsmAnalyzer::operator()(yul::Instruction const& _instruction)
{
checkLooseFeature(
_instruction.location,
"The use of non-functional instructions is disallowed. Please use functional notation instead."
);
solAssert(false, "The use of non-functional instructions is disallowed. Please use functional notation instead.");
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
@ -215,34 +199,21 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement)
{
int initialStackHeight = m_stackHeight;
bool success = boost::apply_visitor(*this, _statement.expression);
if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose))
if (success && m_stackHeight != initialStackHeight)
{
Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError;
string msg =
"Top-level expressions are not supposed to return values (this expression returns " +
to_string(m_stackHeight - initialStackHeight) +
" value" +
(m_stackHeight - initialStackHeight == 1 ? "" : "s") +
"). Use ``pop()`` or assign them.";
m_errorReporter.error(errorType, _statement.location, msg);
if (errorType != Error::Type::Warning)
success = false;
m_errorReporter.error(Error::Type::TypeError, _statement.location, msg);
success = false;
}
m_info.stackHeightInfo[&_statement] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(StackAssignment const& _assignment)
{
checkLooseFeature(
_assignment.location,
"The use of stack assignment is disallowed. Please use assignment in functional notation instead."
);
bool success = checkAssignment(_assignment.variableName, size_t(-1));
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(Assignment const& _assignment)
{
solAssert(_assignment.value, "");
@ -366,7 +337,8 @@ bool AsmAnalyzer::operator()(FunctionCall const& _funCall)
}
)))
{
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
if (!warnOnInstructions(_funCall.functionName.name.str(), _funCall.functionName.location))
m_errorReporter.declarationError(_funCall.functionName.location, "Function not found.");
success = false;
}
if (success)
@ -562,7 +534,7 @@ bool AsmAnalyzer::operator()(Block const& _block)
m_stackHeight -= scope(&_block).numberOfVariables();
int const stackDiff = m_stackHeight - initialStackHeight;
if (stackDiff != 0)
if (success && stackDiff != 0)
{
m_errorReporter.declarationError(
_block.location,
@ -587,7 +559,7 @@ bool AsmAnalyzer::expectExpression(Expression const& _expr)
int const initialHeight = m_stackHeight;
if (!boost::apply_visitor(*this, _expr))
success = false;
if (!expectDeposit(1, initialHeight, locationOf(_expr)))
if (success && !expectDeposit(1, initialHeight, locationOf(_expr)))
success = false;
return success;
}
@ -682,7 +654,16 @@ void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _loc
);
}
void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocation const& _location)
bool AsmAnalyzer::warnOnInstructions(std::string const& _instructionIdentifier, langutil::SourceLocation const& _location)
{
auto const builtin = EVMDialect::strictAssemblyForEVM(EVMVersion{}).builtin(YulString(_instructionIdentifier));
if (builtin)
return warnOnInstructions(builtin->instruction.get(), _location);
else
return false;
}
bool AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocation const& _location)
{
// We assume that returndatacopy, returndatasize and staticcall are either all available
// or all not available.
@ -746,29 +727,16 @@ void AsmAnalyzer::warnOnInstructions(dev::eth::Instruction _instr, SourceLocatio
_instr == dev::eth::Instruction::JUMPDEST
)
{
if (m_dialect.flavour == AsmFlavour::Loose)
m_errorReporter.error(
m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning,
_location,
"Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are discouraged. "
"Please consider using \"switch\", \"if\" or \"for\" statements instead."
);
else
m_errorReporter.error(
Error::Type::SyntaxError,
_location,
"Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are disallowed in strict assembly. "
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
);
m_errorReporter.error(
Error::Type::SyntaxError,
_location,
"Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are disallowed in strict assembly. "
"Use functions, \"switch\", \"if\" or \"for\" statements instead."
);
}
}
else
return false;
void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description)
{
if (m_dialect.flavour != AsmFlavour::Loose)
solAssert(false, _description);
else if (m_errorTypeForLoose)
m_errorReporter.error(*m_errorTypeForLoose, _location, _description);
return true;
}

View File

@ -59,7 +59,6 @@ public:
explicit AsmAnalyzer(
AsmAnalysisInfo& _analysisInfo,
langutil::ErrorReporter& _errorReporter,
boost::optional<langutil::Error::Type> _errorTypeForLoose,
Dialect const& _dialect,
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(),
std::set<YulString> const& _dataNames = {}
@ -68,7 +67,6 @@ public:
m_info(_analysisInfo),
m_errorReporter(_errorReporter),
m_dialect(_dialect),
m_errorTypeForLoose(_errorTypeForLoose),
m_dataNames(_dataNames)
{
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
@ -85,9 +83,7 @@ public:
bool operator()(Literal const& _literal);
bool operator()(Identifier const&);
bool operator()(FunctionalInstruction const& _functionalInstruction);
bool operator()(Label const& _label);
bool operator()(ExpressionStatement const&);
bool operator()(StackAssignment const&);
bool operator()(Assignment const& _assignment);
bool operator()(VariableDeclaration const& _variableDeclaration);
bool operator()(FunctionDefinition const& _functionDefinition);
@ -110,12 +106,8 @@ private:
Scope& scope(Block const* _block);
void expectValidType(std::string const& type, langutil::SourceLocation const& _location);
void warnOnInstructions(dev::eth::Instruction _instr, langutil::SourceLocation const& _location);
/// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler
/// exception (if the flavour is not Loose), reports an error/warning
/// (if m_errorTypeForLoose is set) or does nothing.
void checkLooseFeature(langutil::SourceLocation const& _location, std::string const& _description);
bool warnOnInstructions(dev::eth::Instruction _instr, langutil::SourceLocation const& _location);
bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location);
int m_stackHeight = 0;
yul::ExternalIdentifierAccess::Resolver m_resolver;
@ -127,7 +119,6 @@ private:
langutil::ErrorReporter& m_errorReporter;
langutil::EVMVersion m_evmVersion;
Dialect const& m_dialect;
boost::optional<langutil::Error::Type> m_errorTypeForLoose;
/// Names of data objects to be referenced by builtin functions with literal arguments.
std::set<YulString> m_dataNames;
ForLoop const* m_currentForLoop = nullptr;

View File

@ -49,10 +49,6 @@ enum class LiteralKind { Number, Boolean, String };
struct Literal { langutil::SourceLocation location; LiteralKind kind; YulString value; Type type; };
/// External / internal identifier or label reference
struct Identifier { langutil::SourceLocation location; YulString name; };
/// Jump label ("name:")
struct Label { langutil::SourceLocation location; YulString name; };
/// Assignment from stack (":= x", moves stack top into x, potentially multiple slots)
struct StackAssignment { langutil::SourceLocation location; Identifier variableName; };
/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand
/// side and requires x to occupy exactly one stack slot.
///

View File

@ -30,7 +30,6 @@ namespace yul
struct Instruction;
struct Literal;
struct Label;
struct StackAssignment;
struct Identifier;
struct Assignment;
struct VariableDeclaration;
@ -49,6 +48,6 @@ struct Block;
struct TypedName;
using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
using Statement = boost::variant<ExpressionStatement, Instruction, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Break, Continue, Block>;
}

View File

@ -144,23 +144,6 @@ Statement Parser::parseStatement()
m_scanner->next();
return stmt;
}
case Token::Assign:
{
if (m_dialect.flavour != AsmFlavour::Loose)
break;
StackAssignment assignment = createWithLocation<StackAssignment>();
advance();
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = YulString(currentLiteral());
if (m_dialect.builtin(assignment.variableName.name))
fatalParserError("Identifier expected, got builtin symbol.");
else if (instructions().count(assignment.variableName.name.str()))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
return Statement{move(assignment)};
}
default:
break;
}
@ -222,26 +205,8 @@ Statement Parser::parseStatement()
return Statement{std::move(assignment)};
}
case Token::Colon:
{
if (elementary.type() != typeid(Identifier))
fatalParserError("Label name must precede \":\".");
Identifier const& identifier = boost::get<Identifier>(elementary);
advance();
// label
if (m_dialect.flavour != AsmFlavour::Loose)
fatalParserError("Labels are not supported.");
Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
return label;
}
default:
if (m_dialect.flavour != AsmFlavour::Loose)
fatalParserError("Call or assignment expected.");
fatalParserError("Call or assignment expected.");
break;
}
@ -311,47 +276,8 @@ Expression Parser::parseExpression()
RecursionGuard recursionGuard(*this);
ElementaryOperation operation = parseElementaryOperation();
if (operation.type() == typeid(FunctionCall))
if (operation.type() == typeid(FunctionCall) || currentToken() == Token::LParen)
return parseCall(std::move(operation));
else if (operation.type() == typeid(Instruction))
{
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction const& instr = boost::get<Instruction>(operation);
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
if (
instructionInfo(instr.instruction).ret != 1 ||
isDupInstruction(instr.instruction) ||
isSwapInstruction(instr.instruction)
)
fatalParserError(
"Instruction \"" +
instructionNames().at(instr.instruction) +
"\" not allowed in this context."
);
if (m_dialect.flavour != AsmFlavour::Loose && currentToken() != Token::LParen)
fatalParserError(
"Non-functional instructions are not allowed in this context."
);
// Enforce functional notation for instructions requiring multiple arguments.
int args = instructionInfo(instr.instruction).args;
if (args > 0 && currentToken() != Token::LParen)
fatalParserError(string(
"Expected '(' (instruction \"" +
instructionNames().at(instr.instruction) +
"\" expects " +
to_string(args) +
" arguments)"
));
}
if (currentToken() == Token::LParen)
return parseCall(std::move(operation));
else if (operation.type() == typeid(Instruction))
{
// Instructions not taking arguments are allowed as expressions.
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
Instruction& instr = boost::get<Instruction>(operation);
return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
}
else if (operation.type() == typeid(Identifier))
return boost::get<Identifier>(operation);
else
@ -388,26 +314,12 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
case Token::Address:
{
YulString literal{currentLiteral()};
// first search the set of builtins, then the instructions.
if (m_dialect.builtin(literal))
{
Identifier identifier{location(), literal};
advance();
// If the builtin is not followed by `(` and we are in loose mode,
// fall back to instruction.
if (
m_dialect.flavour == AsmFlavour::Loose &&
instructions().count(identifier.name.str()) &&
m_scanner->currentToken() != Token::LParen
)
return Instruction{identifier.location, instructions().at(identifier.name.str())};
else
return FunctionCall{identifier.location, identifier, {}};
}
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(literal.str()))
{
dev::eth::Instruction const& instr = instructions().at(literal.str());
ret = Instruction{location(), instr};
expectToken(Token::LParen, false);
return FunctionCall{identifier.location, identifier, {}};
}
else
ret = Identifier{location(), literal};
@ -657,8 +569,6 @@ YulString Parser::expectAsmIdentifier()
if (m_dialect.builtin(name))
fatalParserError("Cannot use builtin function name \"" + name.str() + "\" as identifier name.");
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(name.str()))
fatalParserError("Cannot use instruction name \"" + name.str() + "\" as identifier name.");
advance();
return name;
}

View File

@ -113,20 +113,6 @@ string AsmPrinter::operator()(ExpressionStatement const& _statement) const
return boost::apply_visitor(*this, _statement.expression);
}
string AsmPrinter::operator()(Label const& _label) const
{
solAssert(!m_yul, "");
solAssert(!_label.name.empty(), "Invalid label.");
return _label.name.str() + ":";
}
string AsmPrinter::operator()(StackAssignment const& _assignment) const
{
solAssert(!m_yul, "");
solAssert(!_assignment.variableName.name.empty(), "Invalid variable name.");
return "=: " + (*this)(_assignment.variableName);
}
string AsmPrinter::operator()(Assignment const& _assignment) const
{
solAssert(_assignment.variableNames.size() >= 1, "");

View File

@ -41,8 +41,6 @@ public:
std::string operator()(Identifier const& _identifier) const;
std::string operator()(FunctionalInstruction const& _functionalInstruction) const;
std::string operator()(ExpressionStatement const& _expr) const;
std::string operator()(Label const& _label) const;
std::string operator()(StackAssignment const& _assignment) const;
std::string operator()(Assignment const& _assignment) const;
std::string operator()(VariableDeclaration const& _variableDeclaration) const;
std::string operator()(FunctionDefinition const& _functionDefinition) const;

View File

@ -50,20 +50,6 @@ bool ScopeFiller::operator()(ExpressionStatement const& _expr)
return boost::apply_visitor(*this, _expr.expression);
}
bool ScopeFiller::operator()(Label const& _item)
{
if (!m_currentScope->registerLabel(_item.name))
{
//@TODO secondary location
m_errorReporter.declarationError(
_item.location,
"Label name " + _item.name.str() + " already taken in this scope."
);
return false;
}
return true;
}
bool ScopeFiller::operator()(VariableDeclaration const& _varDecl)
{
for (auto const& variable: _varDecl.variables)

View File

@ -54,8 +54,6 @@ public:
bool operator()(Identifier const&) { return true; }
bool operator()(FunctionalInstruction const&) { return true; }
bool operator()(ExpressionStatement const& _expr);
bool operator()(Label const& _label);
bool operator()(StackAssignment const&) { return true; }
bool operator()(Assignment const&) { return true; }
bool operator()(VariableDeclaration const& _variableDeclaration);
bool operator()(FunctionDefinition const& _functionDefinition);

View File

@ -54,7 +54,6 @@ Dialect const& languageToDialect(AssemblyStack::Language _language, EVMVersion _
switch (_language)
{
case AssemblyStack::Language::Assembly:
return EVMDialect::looseAssemblyForEVM(_version);
case AssemblyStack::Language::StrictAssembly:
return EVMDialect::strictAssemblyForEVMObjects(_version);
case AssemblyStack::Language::Yul:
@ -117,7 +116,6 @@ bool AssemblyStack::analyzeParsed(Object& _object)
AsmAnalyzer analyzer(
*_object.analysisInfo,
m_errorReporter,
boost::none,
languageToDialect(m_language, m_evmVersion),
{},
_object.dataNames()
@ -133,15 +131,19 @@ bool AssemblyStack::analyzeParsed(Object& _object)
void AssemblyStack::compileEVM(AbstractAssembly& _assembly, bool _evm15, bool _optimize) const
{
EVMDialect const* dialect = nullptr;
if (m_language == Language::Assembly)
dialect = &EVMDialect::looseAssemblyForEVM(m_evmVersion);
else if (m_language == AssemblyStack::Language::StrictAssembly)
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
else if (m_language == AssemblyStack::Language::Yul)
dialect = &EVMDialect::yulForEVM(m_evmVersion);
else
solAssert(false, "Invalid language.");
switch (m_language)
{
case Language::Assembly:
case Language::StrictAssembly:
dialect = &EVMDialect::strictAssemblyForEVMObjects(m_evmVersion);
break;
case Language::Yul:
dialect = &EVMDialect::yulForEVM(m_evmVersion);
break;
default:
solAssert(false, "Invalid language.");
break;
}
EVMObjectCompiler::compile(*m_parserResult, _assembly, *dialect, _evm15, _optimize);
}

View File

@ -36,7 +36,6 @@ using Type = YulString;
enum class AsmFlavour
{
Loose, // no types, EVM instructions as function, jumps and direct stack manipulations
Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations
Yul // same as Strict mode with types
};
@ -55,7 +54,7 @@ struct BuiltinFunction
struct Dialect: boost::noncopyable
{
AsmFlavour const flavour = AsmFlavour::Loose;
AsmFlavour const flavour = AsmFlavour::Strict;
/// @returns the builtin function of the given name or a nullptr if it is not a builtin function.
virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; }

View File

@ -250,14 +250,6 @@ void CodeTransform::operator()(Assignment const& _assignment)
checkStackHeight(&_assignment);
}
void CodeTransform::operator()(StackAssignment const& _assignment)
{
solAssert(!m_allowStackOpt, "");
m_assembly.setSourceLocation(_assignment.location);
generateAssignment(_assignment.variableName);
checkStackHeight(&_assignment);
}
void CodeTransform::operator()(ExpressionStatement const& _statement)
{
m_assembly.setSourceLocation(_statement.location);
@ -265,17 +257,6 @@ void CodeTransform::operator()(ExpressionStatement const& _statement)
checkStackHeight(&_statement);
}
void CodeTransform::operator()(Label const& _label)
{
solAssert(!m_allowStackOpt, "");
m_assembly.setSourceLocation(_label.location);
solAssert(m_scope, "");
solAssert(m_scope->identifiers.count(_label.name), "");
Scope::Label& label = boost::get<Scope::Label>(m_scope->identifiers.at(_label.name));
m_assembly.appendLabel(labelID(label));
checkStackHeight(&_label);
}
void CodeTransform::operator()(FunctionCall const& _call)
{
solAssert(m_scope, "");

View File

@ -177,8 +177,6 @@ public:
void operator()(FunctionalInstruction const& _instr);
void operator()(FunctionCall const&);
void operator()(ExpressionStatement const& _statement);
void operator()(Label const& _label);
void operator()(StackAssignment const& _assignment);
void operator()(Assignment const& _assignment);
void operator()(VariableDeclaration const& _varDecl);
void operator()(If const& _if);

View File

@ -185,15 +185,6 @@ BuiltinFunctionForEVM const* EVMDialect::builtin(YulString _name) const
return nullptr;
}
EVMDialect const& EVMDialect::looseAssemblyForEVM(langutil::EVMVersion _version)
{
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;
static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }};
if (!dialects[_version])
dialects[_version] = make_unique<EVMDialect>(AsmFlavour::Loose, false, _version);
return *dialects[_version];
}
EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version)
{
static map<langutil::EVMVersion, unique_ptr<EVMDialect const>> dialects;

View File

@ -72,7 +72,6 @@ struct EVMDialect: public Dialect
BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); }
BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); }
static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version);
static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version);
static EVMDialect const& strictAssemblyForEVMObjects(langutil::EVMVersion _version);
static EVMDialect const& yulForEVM(langutil::EVMVersion _version);

View File

@ -658,7 +658,7 @@ Object EVMToEWasmTranslator::run(Object const& _object)
ErrorList errors;
ErrorReporter errorReporter(errors);
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, boost::none, WasmDialect::instance(), {}, _object.dataNames());
AsmAnalyzer analyzer(*ret.analysisInfo, errorReporter, WasmDialect::instance(), {}, _object.dataNames());
if (!analyzer.analyze(*ret.code))
{
// TODO the errors here are "wrong" because they have invalid source references!

View File

@ -108,23 +108,11 @@ wasm::Expression EWasmCodeTransform::operator()(Assignment const& _assignment)
return generateMultiAssignment(move(variableNames), visit(*_assignment.value));
}
wasm::Expression EWasmCodeTransform::operator()(StackAssignment const&)
{
yulAssert(false, "");
return {};
}
wasm::Expression EWasmCodeTransform::operator()(ExpressionStatement const& _statement)
{
return visitReturnByValue(_statement.expression);
}
wasm::Expression EWasmCodeTransform::operator()(Label const&)
{
yulAssert(false, "");
return {};
}
wasm::Expression EWasmCodeTransform::operator()(FunctionalInstruction const& _f)
{
yulAssert(false, "EVM instruction in ewasm code: " + eth::instructionInfo(_f.instruction).name);

View File

@ -44,8 +44,6 @@ public:
wasm::Expression operator()(yul::FunctionalInstruction const& _instr);
wasm::Expression operator()(yul::FunctionCall const&);
wasm::Expression operator()(yul::ExpressionStatement const& _statement);
wasm::Expression operator()(yul::Label const& _label);
wasm::Expression operator()(yul::StackAssignment const& _assignment);
wasm::Expression operator()(yul::Assignment const& _assignment);
wasm::Expression operator()(yul::VariableDeclaration const& _varDecl);
wasm::Expression operator()(yul::If const& _if);

View File

@ -59,18 +59,6 @@ Statement ASTCopier::operator()(Assignment const& _assignment)
};
}
Statement ASTCopier::operator()(StackAssignment const&)
{
assertThrow(false, OptimizerException, "Invalid operation.");
return {};
}
Statement ASTCopier::operator()(Label const&)
{
assertThrow(false, OptimizerException, "Invalid operation.");
return {};
}
Expression ASTCopier::operator()(FunctionCall const& _call)
{
return FunctionCall{

View File

@ -50,8 +50,6 @@ public:
virtual ~StatementCopier() = default;
virtual Statement operator()(ExpressionStatement const& _statement) = 0;
virtual Statement operator()(Instruction const& _instruction) = 0;
virtual Statement operator()(Label const& _label) = 0;
virtual Statement operator()(StackAssignment const& _assignment) = 0;
virtual Statement operator()(Assignment const& _assignment) = 0;
virtual Statement operator()(VariableDeclaration const& _varDecl) = 0;
virtual Statement operator()(If const& _if) = 0;
@ -77,8 +75,6 @@ public:
Expression operator()(FunctionalInstruction const& _instr) override;
Expression operator()(FunctionCall const&) override;
Statement operator()(ExpressionStatement const& _statement) override;
Statement operator()(Label const& _label) override;
Statement operator()(StackAssignment const& _assignment) override;
Statement operator()(Assignment const& _assignment) override;
Statement operator()(VariableDeclaration const& _varDecl) override;
Statement operator()(If const& _if) override;

View File

@ -48,8 +48,6 @@ public:
virtual void operator()(FunctionalInstruction const& _instr);
virtual void operator()(FunctionCall const& _funCall);
virtual void operator()(ExpressionStatement const& _statement);
virtual void operator()(Label const&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(StackAssignment const&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Assignment const& _assignment);
virtual void operator()(VariableDeclaration const& _varDecl);
virtual void operator()(If const& _if);
@ -85,8 +83,6 @@ public:
virtual void operator()(FunctionalInstruction& _instr);
virtual void operator()(FunctionCall& _funCall);
virtual void operator()(ExpressionStatement& _statement);
virtual void operator()(Label&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(StackAssignment&) { assertThrow(false, OptimizerException, ""); }
virtual void operator()(Assignment& _assignment);
virtual void operator()(VariableDeclaration& _varDecl);
virtual void operator()(If& _if);

View File

@ -164,16 +164,6 @@ bool SyntacticallyEqual::statementEqual(Instruction const&, Instruction const&)
assertThrow(false, OptimizerException, "");
}
bool SyntacticallyEqual::statementEqual(Label const&, Label const&)
{
assertThrow(false, OptimizerException, "");
}
bool SyntacticallyEqual::statementEqual(StackAssignment const&, StackAssignment const&)
{
assertThrow(false, OptimizerException, "");
}
bool SyntacticallyEqual::statementEqual(Block const& _lhs, Block const& _rhs)
{
return containerEqual(_lhs.statements, _rhs.statements, [this](Statement const& _lhsStmt, Statement const& _rhsStmt) -> bool {

View File

@ -60,8 +60,6 @@ public:
bool statementEqual(Block const& _lhs, Block const& _rhs);
private:
bool statementEqual(Instruction const& _lhs, Instruction const& _rhs);
bool statementEqual(Label const& _lhs, Label const& _rhs);
bool statementEqual(StackAssignment const& _lhs, StackAssignment const& _rhs);
bool visitDeclaration(TypedName const& _lhs, TypedName const& _rhs);

View File

@ -188,22 +188,22 @@ BOOST_AUTO_TEST_CASE(surplus_input)
BOOST_AUTO_TEST_CASE(simple_instructions)
{
BOOST_CHECK(successParse("{ dup1 dup1 mul dup1 sub pop }"));
BOOST_CHECK(successParse("{ let y := mul(0x10, mul(0x20, mload(0x40)))}"));
}
BOOST_AUTO_TEST_CASE(selfdestruct)
{
BOOST_CHECK(successParse("{ 0x02 selfdestruct }"));
BOOST_CHECK(successParse("{ selfdestruct(0x02) }"));
}
BOOST_AUTO_TEST_CASE(keywords)
{
BOOST_CHECK(successParse("{ 1 2 byte 2 return address pop }"));
BOOST_CHECK(successParse("{ return (byte(1, 2), 2) pop(address()) }"));
}
BOOST_AUTO_TEST_CASE(constants)
{
BOOST_CHECK(successParse("{ 7 8 mul pop }"));
BOOST_CHECK(successParse("{ pop(mul(7, 8)) }"));
}
BOOST_AUTO_TEST_CASE(vardecl)
@ -237,29 +237,14 @@ BOOST_AUTO_TEST_CASE(vardecl_empty)
BOOST_CHECK(successParse("{ let x }"));
}
BOOST_AUTO_TEST_CASE(assignment)
{
BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }"));
}
BOOST_AUTO_TEST_CASE(label)
{
BOOST_CHECK(successParse("{ 7 abc: 8 eq abc jump pop }"));
}
BOOST_AUTO_TEST_CASE(label_complex)
{
BOOST_CHECK(successParse("{ 7 abc: 8 eq jump(abc) jumpi(eq(7, 8), abc) pop }"));
}
BOOST_AUTO_TEST_CASE(functional)
{
BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }"));
BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }"));
}
BOOST_AUTO_TEST_CASE(functional_partial)
{
CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' (instruction \"byte\" expects 2 arguments)");
CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected '(' but got '}'");
}
BOOST_AUTO_TEST_CASE(functional_partial_success)
@ -274,12 +259,12 @@ BOOST_AUTO_TEST_CASE(functional_assignment)
BOOST_AUTO_TEST_CASE(functional_assignment_complex)
{
BOOST_CHECK(successParse("{ let x := 2 x := add(7, mul(6, x)) mul(7, 8) add }"));
BOOST_CHECK(successParse("{ let x := 2 x := add(add(7, mul(6, x)), mul(7, 8)) }"));
}
BOOST_AUTO_TEST_CASE(vardecl_complex)
{
BOOST_CHECK(successParse("{ let y := 2 let x := add(7, mul(6, y)) add mul(7, 8) }"));
BOOST_CHECK(successParse("{ let y := 2 let x := add(add(7, mul(6, y)), mul(7, 8)) }"));
}
BOOST_AUTO_TEST_CASE(variable_use_before_decl)
@ -303,7 +288,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope)
BOOST_AUTO_TEST_CASE(if_statement_invalid)
{
CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected '(' but got '{'");
BOOST_CHECK("{ if calldatasize() {}");
CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected '{' but got reserved keyword 'let'");
@ -333,7 +318,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case)
BOOST_AUTO_TEST_CASE(switch_invalid_expression)
{
CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected.");
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'");
CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
}
@ -369,7 +354,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression)
CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected '{' but got 'Number'");
CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'");
CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'");
CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' (instruction \"mload\" expects 1 arguments)");
CHECK_PARSE_ERROR("{ for {} mload {} {} }", ParserError, "Expected '(' but got '{'");
CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items");
}
@ -463,7 +448,7 @@ BOOST_AUTO_TEST_CASE(functions_in_parallel_scopes)
BOOST_AUTO_TEST_CASE(variable_access_cross_functions)
{
CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found.");
CHECK_PARSE_ERROR("{ let x := 2 function g() { pop(x) } }", DeclarationError, "Identifier not found.");
}
BOOST_AUTO_TEST_CASE(invalid_tuple_assignment)
@ -516,46 +501,6 @@ BOOST_AUTO_TEST_CASE(multiple_assignment)
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(LooseStrictMode)
BOOST_AUTO_TEST_CASE(no_opcodes_in_strict)
{
BOOST_CHECK(successParse("{ pop(callvalue) }"));
BOOST_CHECK(successParse("{ callvalue pop }"));
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected");
SUCCESS_STRICT("{ pop(callvalue()) }");
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Expected '(' but got reserved keyword 'case'");
}
BOOST_AUTO_TEST_CASE(no_labels_in_strict)
{
BOOST_CHECK(successParse("{ a: }"));
CHECK_STRICT_ERROR("{ a: }", ParserError, "Labels are not supported");
}
BOOST_AUTO_TEST_CASE(no_stack_assign_in_strict)
{
BOOST_CHECK(successParse("{ let x 4 =: x }"));
CHECK_STRICT_ERROR("{ let x 4 =: x }", ParserError, "Call or assignment expected.");
}
BOOST_AUTO_TEST_CASE(no_dup_swap_in_strict)
{
BOOST_CHECK(successParse("{ swap1 }"));
CHECK_STRICT_ERROR("{ swap1 }", ParserError, "Call or assignment expected.");
BOOST_CHECK(successParse("{ dup1 pop }"));
CHECK_STRICT_ERROR("{ dup1 pop }", ParserError, "Call or assignment expected.");
BOOST_CHECK(successParse("{ swap2 }"));
CHECK_STRICT_ERROR("{ swap2 }", ParserError, "Call or assignment expected.");
BOOST_CHECK(successParse("{ dup2 pop }"));
CHECK_STRICT_ERROR("{ dup2 pop }", ParserError, "Call or assignment expected.");
CHECK_PARSE_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
}
BOOST_AUTO_TEST_SUITE_END()
BOOST_AUTO_TEST_SUITE(Printing)
BOOST_AUTO_TEST_CASE(print_smoke)
@ -565,12 +510,12 @@ BOOST_AUTO_TEST_CASE(print_smoke)
BOOST_AUTO_TEST_CASE(print_instructions)
{
parsePrintCompare("{\n 7\n 8\n mul\n dup10\n add\n pop\n}");
parsePrintCompare("{ pop(7) }");
}
BOOST_AUTO_TEST_CASE(print_subblock)
{
parsePrintCompare("{\n {\n dup4\n add\n }\n}");
parsePrintCompare("{ { pop(7) } }");
}
BOOST_AUTO_TEST_CASE(print_functional)
@ -578,14 +523,9 @@ BOOST_AUTO_TEST_CASE(print_functional)
parsePrintCompare("{ let x := mul(sload(0x12), 7) }");
}
BOOST_AUTO_TEST_CASE(print_label)
{
parsePrintCompare("{\n loop:\n jump(loop)\n}", true);
}
BOOST_AUTO_TEST_CASE(print_assignments)
{
parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}");
parsePrintCompare("{\n let x := mul(2, 3)\n pop(7)\n x := add(1, 2)\n}");
}
BOOST_AUTO_TEST_CASE(print_multi_assignments)
@ -595,7 +535,7 @@ BOOST_AUTO_TEST_CASE(print_multi_assignments)
BOOST_AUTO_TEST_CASE(print_string_literals)
{
parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n pop\n}");
parsePrintCompare("{ let x := \"\\n'\\xab\\x95\\\"\" }");
}
BOOST_AUTO_TEST_CASE(print_string_literal_unicode)
@ -661,39 +601,21 @@ BOOST_AUTO_TEST_CASE(oversize_string_literals)
CHECK_ASSEMBLE_ERROR("{ let x := \"123456789012345678901234567890123\" }", TypeError, "String literal too long");
}
BOOST_AUTO_TEST_CASE(assignment_after_tag)
{
BOOST_CHECK(successParse("{ let x := 1 { 7 tag: =: x } }"));
}
BOOST_AUTO_TEST_CASE(magic_variables)
{
CHECK_ASSEMBLE_ERROR("{ this pop }", DeclarationError, "Identifier not found");
CHECK_ASSEMBLE_ERROR("{ ecrecover pop }", DeclarationError, "Identifier not found");
BOOST_CHECK(successAssemble("{ let ecrecover := 1 ecrecover pop }"));
CHECK_ASSEMBLE_ERROR("{ pop(this) }", DeclarationError, "Identifier not found");
CHECK_ASSEMBLE_ERROR("{ pop(ecrecover) }", DeclarationError, "Identifier not found");
BOOST_CHECK(successAssemble("{ let ecrecover := 1 pop(ecrecover) }"));
}
BOOST_AUTO_TEST_CASE(stack_variables)
{
BOOST_CHECK(successAssemble("{ let y := 3 { 2 { let x := y } pop} }"));
}
BOOST_AUTO_TEST_CASE(imbalanced_stack)
{
BOOST_CHECK(successAssemble("{ 1 2 mul pop }", false));
CHECK_ASSEMBLE_ERROR("{ 1 }", DeclarationError, "Unbalanced stack at the end of a block: 1 surplus item(s).");
CHECK_ASSEMBLE_ERROR("{ pop }", DeclarationError, "Unbalanced stack at the end of a block: 1 missing item(s).");
BOOST_CHECK(successAssemble("{ let x := 4 7 add }", false));
}
BOOST_AUTO_TEST_CASE(error_tag)
{
CHECK_ERROR("{ jump(invalidJumpLabel) }", true, DeclarationError, "Identifier not found", true);
BOOST_CHECK(successAssemble("{ let y := 3 { let z := 2 { let x := y } } }"));
}
BOOST_AUTO_TEST_CASE(designated_invalid_instruction)
{
BOOST_CHECK(successAssemble("{ invalid }"));
BOOST_CHECK(successAssemble("{ invalid() }"));
}
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
@ -701,16 +623,6 @@ BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_declaration)
CHECK_ASSEMBLE_ERROR("{ let gas := 1 }", ParserError, "Cannot use builtin");
}
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_assignment)
{
CHECK_ASSEMBLE_ERROR("{ 2 =: gas }", ParserError, "Identifier expected, got builtin symbol");
}
BOOST_AUTO_TEST_CASE(inline_assembly_shadowed_instruction_functional_assignment)
{
CHECK_ASSEMBLE_ERROR("{ gas := 2 }", ParserError, "Variable name must precede \":=\"");
}
BOOST_AUTO_TEST_CASE(revert)
{
BOOST_CHECK(successAssemble("{ revert(0, 0) }"));
@ -772,18 +684,10 @@ BOOST_AUTO_TEST_CASE(large_constant)
BOOST_AUTO_TEST_CASE(keccak256)
{
BOOST_CHECK(successAssemble("{ 0 0 keccak256 pop }"));
BOOST_CHECK(successAssemble("{ pop(keccak256(0, 0)) }"));
}
BOOST_AUTO_TEST_CASE(returndatasize)
{
if (!dev::test::Options::get().evmVersion().supportsReturndata())
return;
BOOST_CHECK(successAssemble("{ let r := returndatasize }"));
}
BOOST_AUTO_TEST_CASE(returndatasize_functional)
{
if (!dev::test::Options::get().evmVersion().supportsReturndata())
return;
@ -794,7 +698,7 @@ BOOST_AUTO_TEST_CASE(returndatacopy)
{
if (!dev::test::Options::get().evmVersion().supportsReturndata())
return;
BOOST_CHECK(successAssemble("{ 64 32 0 returndatacopy }"));
BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }"));
}
BOOST_AUTO_TEST_CASE(returndatacopy_functional)
@ -836,27 +740,10 @@ BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", TypeError, "The \"sar\" instruction is only available for Constantinople-compatible VMs");
}
BOOST_AUTO_TEST_CASE(chainid_instanbul_warning)
BOOST_AUTO_TEST_CASE(jump_error)
{
if (dev::test::Options::get().evmVersion().hasChainID())
return;
CHECK_PARSE_WARNING("{ pop(chainid()) }", TypeError, "The \"chainid\" instruction is only available for Istanbul-compatible VMs");
}
BOOST_AUTO_TEST_CASE(selfbalance_instanbul_warning)
{
if (dev::test::Options::get().evmVersion().hasSelfBalance())
return;
CHECK_PARSE_WARNING("{ pop(selfbalance()) }", TypeError, "The \"selfbalance\" instruction is only available for Istanbul-compatible VMs");
}
BOOST_AUTO_TEST_CASE(jump_warning)
{
CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ jump(44) }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ jumpi(44, 2) }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions");
CHECK_PARSE_WARNING("{ jump(44) }", DeclarationError, "Function not found.");
CHECK_PARSE_WARNING("{ jumpi(44, 2) }", DeclarationError, "Function not found.");
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -10274,7 +10274,7 @@ BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor)
// Make memory dirty.
assembly {
for { let i := 0 } lt(i, 64) { i := add(i, 1) } {
mstore(msize, not(0))
mstore(msize(), not(0))
}
}
uint16[3] memory c;
@ -12920,7 +12920,7 @@ BOOST_AUTO_TEST_CASE(snark)
input[3] = p2.Y;
bool success;
assembly {
success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60)
success := call(sub(gas(), 2000), 6, 0, input, 0xc0, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
@ -12936,7 +12936,7 @@ BOOST_AUTO_TEST_CASE(snark)
input[2] = s;
bool success;
assembly {
success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)
success := call(sub(gas(), 2000), 7, 0, input, 0x80, r, 0x60)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}
@ -12964,7 +12964,7 @@ BOOST_AUTO_TEST_CASE(snark)
uint[1] memory out;
bool success;
assembly {
success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
success := call(sub(gas(), 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
// Use "invalid" to make gas estimation work
switch success case 0 { invalid() }
}

View File

@ -121,7 +121,7 @@ BOOST_AUTO_TEST_CASE(assembly_staticcall)
string text = R"(
contract C {
function i() view public {
assembly { pop(staticcall(gas, 1, 2, 3, 4, 5)) }
assembly { pop(staticcall(gas(), 1, 2, 3, 4, 5)) }
}
}
)";

View File

@ -1,13 +0,0 @@
contract C {
function f() public pure {
assembly {
l:
l()
}
}
}
// ----
// SyntaxError: (63-64): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead.
// SyntaxError: (63-64): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
// TypeError: (73-74): Attempt to call label instead of function.

View File

@ -8,4 +8,4 @@ contract C {
}
}
// ----
// TypeError: (86-87): Function k used without being called.
// ParserError: (92-93): Call or assignment expected.

View File

@ -7,5 +7,4 @@ contract C {
}
}
// ----
// SyntaxError: (75-82): The use of non-functional instructions is disallowed. Please use functional notation instead.
// SyntaxError: (95-98): The use of non-functional instructions is disallowed. Please use functional notation instead.
// ParserError: (95-98): Expected '(' but got identifier

View File

@ -6,4 +6,4 @@ contract C {
}
}
// ----
// SyntaxError: (75-82): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
// DeclarationError: (75-79): Function not found.

View File

@ -1,10 +0,0 @@
contract C {
function f() public pure {
assembly {
jump(xy)
}
}
}
// ----
// DeclarationError: (68-70): Identifier not found.
// SyntaxError: (63-71): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.

View File

@ -6,5 +6,4 @@ contract C {
}
}
// ----
// SyntaxError: (75-80): The use of labels is disallowed. Please use "if", "switch", "for" or function calls instead.
// SyntaxError: (75-80): Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are discouraged. Please consider using "switch", "if" or "for" statements instead.
// ParserError: (80-81): Call or assignment expected.

View File

@ -6,5 +6,4 @@ contract C {
}
}
// ----
// SyntaxError: (75-83): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
// DeclarationError: (61-93): Unbalanced stack at the end of a block: 1 surplus item(s).
// TypeError: (75-83): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.

View File

@ -6,5 +6,4 @@ contract C {
}
}
// ----
// SyntaxError: (75-76): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
// DeclarationError: (61-86): Unbalanced stack at the end of a block: 1 surplus item(s).
// ParserError: (85-86): Call or assignment expected.

View File

@ -1,9 +0,0 @@
contract C {
function f() public pure {
assembly {
return := 1
}
}
}
// ----
// ParserError: (70-72): Variable name must precede ":=" in assignment.

View File

@ -1,9 +0,0 @@
contract C {
function f() public pure {
assembly {
return : 1
}
}
}
// ----
// ParserError: (70-71): Label name must precede ":".

View File

@ -6,5 +6,4 @@ contract test {
}
}
// ----
// SyntaxError: (73-76): The use of non-functional instructions is disallowed. Please use functional notation instead.
// DeclarationError: (59-86): Unbalanced stack at the end of a block: 1 missing item(s).
// ParserError: (85-86): Expected '(' but got '}'

View File

@ -6,5 +6,4 @@ contract test {
}
}
// ----
// SyntaxError: (73-74): Top-level expressions are not supposed to return values (this expression returns 1 value). Use ``pop()`` or assign them.
// DeclarationError: (59-84): Unbalanced stack at the end of a block: 1 surplus item(s).
// ParserError: (83-84): Call or assignment expected.

View File

@ -1,9 +1,9 @@
contract C {
function f() public pure {
assembly {
_offset
let x := _offset
}
}
}
// ----
// DeclarationError: (75-82): In variable names _slot and _offset can only be used as a suffix.
// DeclarationError: (84-91): In variable names _slot and _offset can only be used as a suffix.

View File

@ -1,9 +1,9 @@
contract C {
function f() public pure {
assembly {
_slot
let x := _slot
}
}
}
// ----
// DeclarationError: (75-80): In variable names _slot and _offset can only be used as a suffix.
// DeclarationError: (84-89): In variable names _slot and _offset can only be used as a suffix.

View File

@ -9,7 +9,7 @@ contract C {
assembly { x := 7 }
}
function g() view public {
assembly { for {} 1 { pop(sload(0)) } { } pop(gas) }
assembly { for {} 1 { pop(sload(0)) } { } pop(gas()) }
}
function h() view public {
assembly { function g() { pop(blockhash(20)) } }
@ -18,6 +18,6 @@ contract C {
assembly { pop(call(0, 1, 2, 3, 4, 5, 6)) }
}
function k() public {
assembly { pop(call(gas, 1, 2, 3, 4, 5, 6)) }
assembly { pop(call(gas(), 1, 2, 3, 4, 5, 6)) }
}
}

View File

@ -61,7 +61,6 @@ bool parse(string const& _source, Dialect const& _dialect, ErrorReporter& errorR
return (yul::AsmAnalyzer(
analysisInfo,
errorReporter,
boost::none,
_dialect
)).analyze(*parserResult);
}
@ -208,11 +207,6 @@ BOOST_AUTO_TEST_CASE(tuple_assignment)
BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }"));
}
BOOST_AUTO_TEST_CASE(label)
{
CHECK_ERROR("{ label: }", ParserError, "Labels are not supported.");
}
BOOST_AUTO_TEST_CASE(instructions)
{
CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected.");
@ -549,11 +543,9 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
SimpleDialect dialect;
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Variable name must precede \":=\" in assignment.", dialect);
CHECK_ERROR_DIALECT("{ function f(x) { f(builtin) } }", ParserError, "Expected '(' but got ')'", dialect);
CHECK_ERROR_DIALECT("{ function f(builtin) {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function f() -> builtin {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Variable name must precede \",\" in multiple assignment.", dialect);
}
BOOST_AUTO_TEST_CASE(builtins_analysis)

View File

@ -106,7 +106,6 @@ public:
AsmAnalyzer analyzer(
*m_analysisInfo,
errorReporter,
langutil::Error::Type::SyntaxError,
m_dialect
);
if (!analyzer.analyze(*m_ast) || !errorReporter.errors().empty())