Skip to content

Error and ICE reporting tweaks #15907

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Compiler Features:


Bugfixes:
* Commandline Interface: Report StackTooDeep errors in compiler mode as proper errors instead of printing diagnostic information meant for internal compiler errors.
* Error Reporting: Fix error locations not being shown for source files with empty names.
* General: Fix internal compiler error when requesting IR AST outputs for interfaces and abstract contracts.
* Metadata: Fix custom cleanup sequence missing from metadata when other optimizer settings have default values.
* SMTChecker: Fix internal compiler error when analyzing overflowing expressions or bitwise negation of unsigned types involving constants.
Expand Down
6 changes: 3 additions & 3 deletions liblangutil/SourceReferenceFormatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@ AnsiColorized SourceReferenceFormatter::diagColored() const

void SourceReferenceFormatter::printSourceLocation(SourceReference const& _ref)
{
if (_ref.sourceName.empty())
return; // Nothing we can print here

if (_ref.position.line < 0)
{
if (_ref.sourceName.empty())
return; // Nothing we can print here

frameColored() << "-->";
m_stream << ' ' << _ref.sourceName << '\n';
return; // No line available, nothing else to print
Expand Down
7 changes: 6 additions & 1 deletion libsolidity/interface/CompilerStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,12 @@ YulStack CompilerStack::loadGeneratedIR(std::string const& _ir) const
yulAnalysisSuccessful,
_ir + "\n\n"
"Invalid IR generated:\n" +
SourceReferenceFormatter::formatErrorInformation(stack.errors(), stack) + "\n"
SourceReferenceFormatter::formatErrorInformation(
stack.errors(),
stack, // _charStreamProvider
false, // _colored
true // _withErrorIds
) + "\n"
);

return stack;
Expand Down
36 changes: 22 additions & 14 deletions libsolidity/interface/StandardCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ Json formatErrorWithException(
);

if (std::string const* description = _exception.comment())
message = ((_message.length() > 0) ? (_message + ":") : "") + *description;
message = ((_message.length() > 0) ? (_message + ": ") : "") + *description;
else
message = _message;

Expand Down Expand Up @@ -1412,6 +1412,7 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
));
}
}
// NOTE: This includes langutil::StackTooDeepError.
catch (CompilerError const& _exception)
{
errors.emplace_back(formatErrorWithException(
Expand All @@ -1422,39 +1423,43 @@ Json StandardCompiler::compileSolidity(StandardCompiler::InputsAndSettings _inpu
"Compiler error (" + _exception.lineInfo() + ")"
));
}
catch (InternalCompilerError const& _exception)
catch (yul::StackTooDeepError const& _exception)
{
errors.emplace_back(formatErrorWithException(
compilerStack,
_exception,
Error::Type::YulException,
"general",
"" // No prefix needed. These messages already say it's a "stack too deep" error.
));
}
catch (InternalCompilerError const&)
{
errors.emplace_back(formatError(
Error::Type::InternalCompilerError,
"general",
"Internal compiler error (" + _exception.lineInfo() + ")"
"Internal compiler error:\n" + boost::current_exception_diagnostic_information()
));
}
catch (UnimplementedFeatureError const& _exception)
{
// let StandardCompiler::compile handle this
throw _exception;
}
catch (yul::YulException const& _exception)
catch (YulAssertion const&)
{
errors.emplace_back(formatErrorWithException(
compilerStack,
_exception,
errors.emplace_back(formatError(
Error::Type::YulException,
"general",
"Yul exception"
"Yul assertion failed:\n" + boost::current_exception_diagnostic_information()
));
}
catch (smtutil::SMTLogicError const& _exception)
catch (smtutil::SMTLogicError const&)
{
errors.emplace_back(formatErrorWithException(
compilerStack,
_exception,
errors.emplace_back(formatError(
Error::Type::SMTLogicException,
"general",
"SMT logic exception"
"SMT logic error:\n" + boost::current_exception_diagnostic_information()
));
}
catch (...)
Expand Down Expand Up @@ -1827,7 +1832,10 @@ Json StandardCompiler::compile(Json const& _input) noexcept
}
catch (...)
{
return formatFatalError(Error::Type::InternalCompilerError, "Internal exception in StandardCompiler::compile: " + boost::current_exception_diagnostic_information());
return formatFatalError(
Error::Type::InternalCompilerError,
"Uncaught exception:\n" + boost::current_exception_diagnostic_information()
);
}
}

Expand Down
31 changes: 30 additions & 1 deletion libyul/AsmAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,36 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(
{},
std::move(_objectStructure)
).analyze(_astRoot);
yulAssert(success && !errors.hasErrors(), "Invalid assembly/yul code.");

if (!success)
{
auto formatErrors = [](ErrorList const& _errorList) {
std::vector<std::string> formattedErrors;
for (std::shared_ptr<Error const> const& error: _errorList)
{
yulAssert(error->comment());
formattedErrors.push_back(fmt::format(
// Intentionally not showing source locations because we don't have the original
// source here and it's unlikely they match the pretty-printed version.
// They may not even match the original source if the AST was modified by the optimizer.
"- {} {}: {}",
Error::formatErrorType(error->type()),
error->errorId().error,
*error->comment()
));
}
return joinHumanReadable(formattedErrors, "\n");
};

yulAssert(errors.hasErrors(), "Yul analysis failed but did not report any errors.");
yulAssert(false, fmt::format(
"{}\n\nExpected valid Yul, but errors were reported during analysis:\n{}",
AsmPrinter{_dialect}(_astRoot),
formatErrors(errorList)
));
}

yulAssert(!errors.hasErrors());
return analysisInfo;
}

Expand Down
2 changes: 1 addition & 1 deletion libyul/Exceptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace solidity::yul
struct YulException: virtual util::Exception {};
struct OptimizerException: virtual YulException {};
struct CodegenException: virtual YulException {};
struct YulAssertion: virtual YulException {};
struct YulAssertion: virtual util::Exception {};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should simplify this one day and make all assert macros throw InternalCompilerError. Having different types and even documenting them in Error types just unnecessarily exposes implementation details to the user. We should only have compilation errors there. It's especially confusing when things like SMTLogicError are actually failed assertions.


struct StackTooDeepError: virtual YulException
{
Expand Down
10 changes: 10 additions & 0 deletions solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,7 @@ void CommandLineInterface::compile()
if (!successful)
solThrow(CommandLineExecutionError, "");
}
// NOTE: This includes langutil::StackTooDeepError.
catch (CompilerError const& _exception)
{
m_hasOutput = true;
Expand All @@ -1018,6 +1019,15 @@ void CommandLineInterface::compile()
);
solThrow(CommandLineExecutionError, "");
}
catch (yul::StackTooDeepError const& _exception)
{
m_hasOutput = true;
formatter.printExceptionInformation(
_exception,
Error::errorSeverity(Error::Type::YulException)
);
solThrow(CommandLineExecutionError, "");
}
}

void CommandLineInterface::handleCombinedJSON()
Expand Down
6 changes: 6 additions & 0 deletions solc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ int main(int argc, char** argv)
std::cerr << boost::diagnostic_information(_exception);
return 2;
}
catch (yul::YulAssertion const& _exception)
{
std::cerr << "Yul assertion failed:" << std::endl;
std::cerr << boost::diagnostic_information(_exception);
return 2;
}
catch (...)
{
std::cerr << "Uncaught exception:" << std::endl;
Expand Down
8 changes: 4 additions & 4 deletions test/CommonSyntaxTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,15 @@ void CommonSyntaxTest::printErrorList(
for (auto const& error: _errorList)
{
{
util::AnsiColorized scope(
util::AnsiColorized formattedStream(
_stream,
_formatted,
{BOLD, SourceReferenceFormatter::errorTextColor(Error::errorSeverity(error.type))}
);
_stream << _linePrefix << Error::formatErrorType(error.type);
formattedStream << _linePrefix << Error::formatErrorType(error.type);
if (error.errorId.has_value())
_stream << ' ' << error.errorId->error;
_stream << ": ";
formattedStream << ' ' << error.errorId->error;
formattedStream << ": ";
}
Comment on lines -200 to 209
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one does not actually fix anything but I decided to change it to be less of a head-scratcher.

We usually output to the ANSI stream using <<, but here it's just created and we ignore it. I initially thought it was a bug and that coloring must be broken because of it, but it works. Apparently the control codes are issued at construction and destruction time.

if (!error.sourceName.empty() || error.locationStart >= 0 || error.locationEnd >= 0)
{
Expand Down
1 change: 1 addition & 0 deletions test/cmdlineTests/stack_too_deep_from_code_transform/args
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--via-ir --bin
6 changes: 6 additions & 0 deletions test/cmdlineTests/stack_too_deep_from_code_transform/err
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Error: Cannot swap Variable value23 with Slot 0x20: too deep in the stack by 9 slots in [ RET value23 value22 value21 value20 value19 value18 value17 value16 value15 value14 value13 value12 value11 value10 value9 value8 value7 value6 value5 value4 value3 value2 value1 value0 pos 0x20 ]
memoryguard was present.
--> stack_too_deep_from_code_transform/input.sol:1:1:
|
1 | contract C {
| ^ (Relevant source part starts here and spans across multiple lines).
Comment on lines +1 to +6
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what it looked like before the change:

Uncaught exception:
/solidity/libyul/backends/evm/EVMObjectCompiler.cpp(110): Throw in function void solidity::yul::EVMObjectCompiler::run(const solidity::yul::Object&, bool)
Dynamic exception type:
std::exception::what: Cannot swap Variable value23 with Slot 0x20: too deep in the stack by 9 slots in [ RET value23 value22 value21 value20 value19 value18 value17 value16 value15 value14 value13 value12 value11 value10 value9 value8 value7 value6 value5 value4 value3 value2 value1 value0 pos 0x20 ]
memoryguard was present.
[solidity::util::tag_comment*] = Cannot swap Variable value23 with Slot 0x20: too deep in the stack by 9 slots in [ RET value23 value22 value21 value20 value19 value18 value17 value16 value15 value14 value13 value12 value11 value10 value9 value8 value7 value6 value5 value4 value3 value2 value1 value0 pos 0x20 ]
memoryguard was present.
[solidity::langutil::tag_sourceLocation*] = stack_too_deep_from_code_transform/input.sol[0,206]

1 change: 1 addition & 0 deletions test/cmdlineTests/stack_too_deep_from_code_transform/exit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
10 changes: 10 additions & 0 deletions test/cmdlineTests/stack_too_deep_from_code_transform/input.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public {
uint b;
abi.encodePacked(
b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b
);
}
}
4 changes: 4 additions & 0 deletions test/cmdlineTests/standard_empty_file_name/output.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
"component": "general",
"errorCode": "2904",
"formattedMessage": "DeclarationError: Declaration \"A\" not found in \"\" (referenced as \".\").
--> :2:24:
|
2 | pragma solidity >=0.0; import {A} from \".\";
| ^^^^^^^^^^^^^^^^^^^^
Comment on lines 6 to +10
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this affects also the errors reported by the assert in CompilerStack::loadGeneratedIR().

Instead of this:

Invalid IR generated:
Error (6913): Call or assignment expected.

Error (8143): Expected keyword "data" or "object" or "}".

we'll now see this:

Invalid IR generated:
Error (6913): Call or assignment expected.
 --> :7:9:
  |
7 |         mstore(64, memoryguard(128))
  |         ^^^^^^

Error (8143): Expected keyword "data" or "object" or "}".
 --> :7:9:
  |
7 |         mstore(64, memoryguard(128))
  |         ^^^^^^

Another thing it affects are Yul test case variants that use the empty name for the source they parse and don't allow errors in expectations. The formatted errors are instead included in the exception they throw.


",
"message": "Declaration \"A\" not found in \"\" (referenced as \".\").",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
contract C {
function f() public {
uint b;
abi.encodePacked(
b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b,
b, b, b, b, b, b, b, b
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"language": "Solidity",
"sources": {
"in.sol": {"urls": ["standard_stack_too_deep_from_code_transform/in.sol"]}
},
"settings": {
"viaIR": true,
"outputSelection": {
"*": {"*": ["evm.bytecode"]}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"errors": [
{
"component": "general",
"formattedMessage": "YulException: Cannot swap Variable value23 with Slot 0x20: too deep in the stack by 9 slots in [ RET value23 value22 value21 value20 value19 value18 value17 value16 value15 value14 value13 value12 value11 value10 value9 value8 value7 value6 value5 value4 value3 value2 value1 value0 pos 0x20 ]
memoryguard was present.
--> in.sol:1:1:
|
1 | contract C {
| ^ (Relevant source part starts here and spans across multiple lines).

",
"message": "Cannot swap Variable value23 with Slot 0x20: too deep in the stack by 9 slots in [ RET value23 value22 value21 value20 value19 value18 value17 value16 value15 value14 value13 value12 value11 value10 value9 value8 value7 value6 value5 value4 value3 value2 value1 value0 pos 0x20 ]
memoryguard was present.",
"severity": "error",
"sourceLocation": {
"end": 206,
"file": "in.sol",
"start": 0
},
"type": "YulException"
}
],
"sources": {
"in.sol": {
"id": 0
}
}
}