Skip to content

Commit

Permalink
Don't ignore errors reported at optimization and assembling steps in …
Browse files Browse the repository at this point in the history
…Yul compilation
  • Loading branch information
cameel committed Dec 11, 2024
1 parent 939e77c commit 2f6d9ab
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 56 deletions.
38 changes: 35 additions & 3 deletions libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -784,14 +784,17 @@ bool CompilerStack::compile(State _stopAfter)
// but CodeGenerationError is one case where someone decided to just throw Error.
solAssert(_error.type() == Error::Type::CodeGenerationError);
m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what());
return false;
}
catch (UnimplementedFeatureError const& _error)
{
reportUnimplementedFeatureError(_error);
return false;
}

if (m_errorReporter.hasErrors())
return false;
}

solAssert(!m_errorReporter.hasErrors());
m_stackState = CompilationSuccessful;
this->link();
return true;
Expand Down Expand Up @@ -1611,6 +1614,14 @@ void CompilerStack::generateEVMFromIR(ContractDefinition const& _contract)
std::string deployedName = IRNames::deployedObject(_contract);
solAssert(!deployedName.empty(), "");
tie(compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly) = stack.assembleEVMWithDeployed(deployedName);

if (stack.hasErrors())
{
for (std::shared_ptr<Error const> const& error: stack.errors())
reportIRPostAnalysisError(error.get());
return;
}

assembleYul(_contract, compiledContract.evmAssembly, compiledContract.evmRuntimeAssembly);
}

Expand Down Expand Up @@ -2005,6 +2016,27 @@ experimental::Analysis const& CompilerStack::experimentalAnalysis() const

void CompilerStack::reportUnimplementedFeatureError(UnimplementedFeatureError const& _error)
{
solAssert(_error.comment(), "Unimplemented feature errors must include a message for the user");
solAssert(_error.comment(), "Errors must include a message for the user.");

m_errorReporter.unimplementedFeatureError(1834_error, _error.sourceLocation(), *_error.comment());
}

void CompilerStack::reportIRPostAnalysisError(Error const* _error)
{
solAssert(_error);
solAssert(_error->comment(), "Errors must include a message for the user.");
solAssert(!_error->secondarySourceLocation());

// Do not report Yul warnings and infos. These are only reported in pure Yul compilation.
if (!Error::isError(_error->severity()))
return;

m_errorReporter.error(
_error->errorId(),
_error->type(),
// Ignore the original location. It's likely missing, but even if not, it points at Yul source.
// CompilerStack can only point at locations in Solidity sources.
SourceLocation{},
*_error->comment()
);
}
1 change: 1 addition & 0 deletions libsolidity/interface/CompilerStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ class CompilerStack: public langutil::CharStreamProvider, public evmasm::Abstrac
) const;

void reportUnimplementedFeatureError(langutil::UnimplementedFeatureError const& _error);
void reportIRPostAnalysisError(langutil::Error const* _error);

ReadCallback::Callback m_readFile;
OptimiserSettings m_optimiserSettings;
Expand Down
56 changes: 29 additions & 27 deletions libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1616,9 +1616,35 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
std::string const& sourceName = _inputsAndSettings.sources.begin()->first;
std::string const& sourceContents = _inputsAndSettings.sources.begin()->second;

// Inconsistent state - stop here to receive error reports from users
if (!stack.parseAndAnalyze(sourceName, sourceContents) && !stack.hasErrors())
solAssert(false, "No error reported, but parsing/analysis failed.");
std::string contractName;
bool const wildcardMatchesExperimental = true;
MachineAssemblyObject object;
MachineAssemblyObject deployedObject;

bool successful = stack.parseAndAnalyze(sourceName, sourceContents);
if (!successful)
// Inconsistent state - stop here to receive error reports from users
solAssert(stack.hasErrors(), "No error reported, but parsing/analysis failed.");
else
{
contractName = stack.parserResult()->name;
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
output["contracts"][sourceName][contractName]["ir"] = stack.print();

if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ast", wildcardMatchesExperimental))
{
Json sourceResult;
sourceResult["id"] = 0;
sourceResult["ast"] = stack.astJson();
output["sources"][sourceName] = sourceResult;
}
stack.optimize();
std::tie(object, deployedObject) = stack.assembleWithDeployed();
if (object.bytecode)
object.bytecode->link(_inputsAndSettings.libraries);
if (deployedObject.bytecode)
deployedObject.bytecode->link(_inputsAndSettings.libraries);
}

for (auto const& error: stack.errors())
{
Expand All @@ -1635,30 +1661,6 @@ Json StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings)
if (stack.hasErrors())
return output;

std::string contractName = stack.parserResult()->name;

bool const wildcardMatchesExperimental = true;
if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ir", wildcardMatchesExperimental))
output["contracts"][sourceName][contractName]["ir"] = stack.print();

if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "ast", wildcardMatchesExperimental))
{
Json sourceResult;
sourceResult["id"] = 0;
sourceResult["ast"] = stack.astJson();
output["sources"][sourceName] = sourceResult;
}
stack.optimize();

MachineAssemblyObject object;
MachineAssemblyObject deployedObject;
std::tie(object, deployedObject) = stack.assembleWithDeployed();

if (object.bytecode)
object.bytecode->link(_inputsAndSettings.libraries);
if (deployedObject.bytecode)
deployedObject.bytecode->link(_inputsAndSettings.libraries);

for (auto&& [kind, isDeployed]: {make_pair("bytecode"s, false), make_pair("deployedBytecode"s, true)})
if (isArtifactRequested(
_inputsAndSettings.outputSelection,
Expand Down
15 changes: 11 additions & 4 deletions libyul/YulStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,9 +261,14 @@ MachineAssemblyObject YulStack::assemble(Machine _machine)
std::pair<MachineAssemblyObject, MachineAssemblyObject>
YulStack::assembleWithDeployed(std::optional<std::string_view> _deployName)
{
yulAssert(m_charStream);

auto [creationAssembly, deployedAssembly] = assembleEVMWithDeployed(_deployName);
yulAssert(creationAssembly, "");
yulAssert(m_charStream, "");
if (!creationAssembly)
{
yulAssert(!deployedAssembly);
return {MachineAssemblyObject{}, MachineAssemblyObject{}};
}

MachineAssemblyObject creationObject;
MachineAssemblyObject deployedObject;
Expand Down Expand Up @@ -297,6 +302,7 @@ YulStack::assembleWithDeployed(std::optional<std::string_view> _deployName)
catch (UnimplementedFeatureError const& _error)
{
reportUnimplementedFeatureError(_error);
return {MachineAssemblyObject{}, MachineAssemblyObject{}};
}

return {std::move(creationObject), std::move(deployedObject)};
Expand Down Expand Up @@ -353,9 +359,10 @@ YulStack::assembleEVMWithDeployed(std::optional<std::string_view> _deployName)
catch (UnimplementedFeatureError const& _error)
{
reportUnimplementedFeatureError(_error);
return {nullptr, nullptr};
}

return {std::make_shared<evmasm::Assembly>(assembly), {}};
return {std::make_shared<evmasm::Assembly>(assembly), nullptr};
}

std::string YulStack::print() const
Expand Down Expand Up @@ -427,6 +434,6 @@ std::shared_ptr<Object> YulStack::parserResult() const

void YulStack::reportUnimplementedFeatureError(UnimplementedFeatureError const& _error)
{
solAssert(_error.comment(), "Unimplemented feature errors must include a message for the user");
yulAssert(_error.comment(), "Errors must include a message for the user.");
m_errorReporter.unimplementedFeatureError(1920_error, _error.sourceLocation(), *_error.comment());
}
38 changes: 22 additions & 16 deletions solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1232,9 +1232,10 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y

bool successful = true;
std::map<std::string, yul::YulStack> yulStacks;
for (auto const& src: m_fileReader.sourceUnits())
std::map<std::string, yul::MachineAssemblyObject> objects;
for (auto const& [sourceUnitName, yulSource]: m_fileReader.sourceUnits())
{
auto& stack = yulStacks[src.first] = yul::YulStack(
auto& stack = yulStacks[sourceUnitName] = yul::YulStack(
m_options.output.evmVersion,
m_options.output.eofVersion,
_language,
Expand All @@ -1244,20 +1245,28 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
DebugInfoSelection::Default()
);

if (!stack.parseAndAnalyze(src.first, src.second))
successful = false;
successful = successful && stack.parseAndAnalyze(sourceUnitName, yulSource);
if (!successful)
solAssert(stack.hasErrors(), "No error reported, but parsing/analysis failed.");
else
stack.optimize();

if (successful && m_options.compiler.outputs.asmJson)
{
std::shared_ptr<yul::Object> result = stack.parserResult();
if (result && !result->hasContiguousSourceIndices())
if (
m_options.compiler.outputs.asmJson &&
stack.parserResult() &&
!stack.parserResult()->hasContiguousSourceIndices()
)
solThrow(
CommandLineExecutionError,
"Generating the assembly JSON output was not possible. "
"Source indices provided in the @use-src annotation in the Yul input do not start at 0 or are not contiguous."
);

stack.optimize();

yul::MachineAssemblyObject object = stack.assemble(_targetMachine);
if (object.bytecode)
object.bytecode->link(m_options.linker.libraries);
objects.insert({sourceUnitName, std::move(object)});
}
}

Expand All @@ -1281,13 +1290,14 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
solThrow(CommandLineExecutionError, "");
}

for (auto const& src: m_fileReader.sourceUnits())
for (auto const& [sourceUnitName, yulSource]: m_fileReader.sourceUnits())
{
solAssert(_targetMachine == yul::YulStack::Machine::EVM);
std::string machine = "EVM";
sout() << std::endl << "======= " << src.first << " (" << machine << ") =======" << std::endl;
sout() << std::endl << "======= " << sourceUnitName << " (" << machine << ") =======" << std::endl;

yul::YulStack& stack = yulStacks[src.first];
yul::YulStack const& stack = yulStacks[sourceUnitName];
yul::MachineAssemblyObject const& object = objects[sourceUnitName];

if (m_options.compiler.outputs.irOptimized)
{
Expand All @@ -1297,10 +1307,6 @@ void CommandLineInterface::assembleYul(yul::YulStack::Language _language, yul::Y
sout() << stack.print() << std::endl;
}

yul::MachineAssemblyObject object;
object = stack.assemble(_targetMachine);
object.bytecode->link(m_options.linker.libraries);

if (m_options.compiler.outputs.binary)
{
sout() << std::endl << "Binary representation:" << std::endl;
Expand Down
16 changes: 10 additions & 6 deletions test/libyul/ObjectCompilerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,18 +71,22 @@ TestCase::TestResult ObjectCompilerTest::run(std::ostream& _stream, std::string
OptimiserSettings::preset(m_optimisationPreset),
DebugInfoSelection::All()
);
if (!stack.parseAndAnalyze("source", m_source))
bool successful = stack.parseAndAnalyze("source", m_source);
MachineAssemblyObject obj;
if (successful)
{
stack.optimize();
obj = stack.assemble(YulStack::Machine::EVM);
}
if (stack.hasErrors())
{
AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << std::endl;
SourceReferenceFormatter{_stream, stack, true, false}
.printErrorInformation(stack.errors());
return TestResult::FatalError;
}
stack.optimize();

MachineAssemblyObject obj = stack.assemble(YulStack::Machine::EVM);
solAssert(obj.bytecode, "");
solAssert(obj.sourceMappings, "");
solAssert(obj.bytecode);
solAssert(obj.sourceMappings);

m_obtainedResult = "Assembly:\n" + obj.assembly->assemblyString(stack.debugInfoSelection());
if (obj.bytecode->bytecode.empty())
Expand Down

0 comments on commit 2f6d9ab

Please sign in to comment.