From 67514e1b19efe0bf037f34fa14a3cc5555e33468 Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 06:12:06 -0400 Subject: [PATCH 1/8] Update expected error output for test cases --- test/lang/multivalues/assignmismatch/1/compilererr.txt | 2 +- test/lang/multivalues/assignmismatch/2/compilererr.txt | 2 +- test/lang/multivalues/assignmismatch/3/compilererr.txt | 2 +- test/lang/multivalues/assignmismatch/4/compilererr.txt | 2 +- test/lang/multivalues/assignmismatch/5/compilererr.txt | 2 +- test/lang/multivalues/bindingmismatch/1/compilererr.txt | 2 +- test/lang/multivalues/bindingmismatch/2/compilererr.txt | 2 +- test/lang/multivalues/bindingmismatch/3/compilererr.txt | 2 +- test/lang/multivalues/bindingmismatch/4/compilererr.txt | 2 +- test/lang/multivalues/bindingmismatch/5/compilererr.txt | 2 +- test/regression/279/compilererr.txt | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/test/lang/multivalues/assignmismatch/1/compilererr.txt b/test/lang/multivalues/assignmismatch/1/compilererr.txt index 274b51452..a4e123361 100644 --- a/test/lang/multivalues/assignmismatch/1/compilererr.txt +++ b/test/lang/multivalues/assignmismatch/1/compilererr.txt @@ -1 +1 @@ -left side has 1 value, but right side has 0 values +left side has 1 value, right side has 0 values diff --git a/test/lang/multivalues/assignmismatch/2/compilererr.txt b/test/lang/multivalues/assignmismatch/2/compilererr.txt index 34bc51bf1..ef2df009a 100644 --- a/test/lang/multivalues/assignmismatch/2/compilererr.txt +++ b/test/lang/multivalues/assignmismatch/2/compilererr.txt @@ -1 +1 @@ -left side has 1 value, but right side has 2 values +left side has 1 value, right side has 2 values diff --git a/test/lang/multivalues/assignmismatch/3/compilererr.txt b/test/lang/multivalues/assignmismatch/3/compilererr.txt index 357155428..28a38a253 100644 --- a/test/lang/multivalues/assignmismatch/3/compilererr.txt +++ b/test/lang/multivalues/assignmismatch/3/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 0 values +left side has 2 values, right side has 0 values diff --git a/test/lang/multivalues/assignmismatch/4/compilererr.txt b/test/lang/multivalues/assignmismatch/4/compilererr.txt index 6553c8bab..f5371dcce 100644 --- a/test/lang/multivalues/assignmismatch/4/compilererr.txt +++ b/test/lang/multivalues/assignmismatch/4/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 1 value +left side has 2 values, right side has 1 value diff --git a/test/lang/multivalues/assignmismatch/5/compilererr.txt b/test/lang/multivalues/assignmismatch/5/compilererr.txt index 476c43f54..80e9abf33 100644 --- a/test/lang/multivalues/assignmismatch/5/compilererr.txt +++ b/test/lang/multivalues/assignmismatch/5/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 3 values +left side has 2 values, right side has 3 values diff --git a/test/lang/multivalues/bindingmismatch/1/compilererr.txt b/test/lang/multivalues/bindingmismatch/1/compilererr.txt index 274b51452..a4e123361 100644 --- a/test/lang/multivalues/bindingmismatch/1/compilererr.txt +++ b/test/lang/multivalues/bindingmismatch/1/compilererr.txt @@ -1 +1 @@ -left side has 1 value, but right side has 0 values +left side has 1 value, right side has 0 values diff --git a/test/lang/multivalues/bindingmismatch/2/compilererr.txt b/test/lang/multivalues/bindingmismatch/2/compilererr.txt index 34bc51bf1..ef2df009a 100644 --- a/test/lang/multivalues/bindingmismatch/2/compilererr.txt +++ b/test/lang/multivalues/bindingmismatch/2/compilererr.txt @@ -1 +1 @@ -left side has 1 value, but right side has 2 values +left side has 1 value, right side has 2 values diff --git a/test/lang/multivalues/bindingmismatch/3/compilererr.txt b/test/lang/multivalues/bindingmismatch/3/compilererr.txt index 357155428..28a38a253 100644 --- a/test/lang/multivalues/bindingmismatch/3/compilererr.txt +++ b/test/lang/multivalues/bindingmismatch/3/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 0 values +left side has 2 values, right side has 0 values diff --git a/test/lang/multivalues/bindingmismatch/4/compilererr.txt b/test/lang/multivalues/bindingmismatch/4/compilererr.txt index 6553c8bab..f5371dcce 100644 --- a/test/lang/multivalues/bindingmismatch/4/compilererr.txt +++ b/test/lang/multivalues/bindingmismatch/4/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 1 value +left side has 2 values, right side has 1 value diff --git a/test/lang/multivalues/bindingmismatch/5/compilererr.txt b/test/lang/multivalues/bindingmismatch/5/compilererr.txt index 099e7523b..e7d50341e 100644 --- a/test/lang/multivalues/bindingmismatch/5/compilererr.txt +++ b/test/lang/multivalues/bindingmismatch/5/compilererr.txt @@ -1 +1 @@ -left side has 2 values, but right side has 3 values \ No newline at end of file +left side has 2 values, right side has 3 values \ No newline at end of file diff --git a/test/regression/279/compilererr.txt b/test/regression/279/compilererr.txt index 27ea85444..09af3c0b2 100644 --- a/test/regression/279/compilererr.txt +++ b/test/regression/279/compilererr.txt @@ -1 +1 @@ -main\.crm\(2,27\): error: expected 2 values, but received 1 value +error: expected 2 values, found 1\n at .*main\.crm:2:28 From 2b902cf2b78ee7bf0907c18fb8952d8470b5e35f Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 06:56:27 -0400 Subject: [PATCH 2/8] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 750140e95..3d082f4d0 100644 --- a/.gitignore +++ b/.gitignore @@ -32,4 +32,4 @@ cscope.out .gdb_history site/ __pycache__/ - +doc \ No newline at end of file From 16aca9d51a913d4d7a1ceb7f5928ad63745fb93c Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:42:05 -0400 Subject: [PATCH 3/8] Add Span type to ceramic.hpp --- compiler/ceramic.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/compiler/ceramic.hpp b/compiler/ceramic.hpp index 3d5e8d255..9363bbc70 100644 --- a/compiler/ceramic.hpp +++ b/compiler/ceramic.hpp @@ -598,6 +598,25 @@ struct Location { bool ok() const { return source != nullptr; } }; +// half-open [startOffset, endOffset) range over a source buffer. +// zero-width spans (start == end) render as a single caret. +struct Span { + SourcePtr source; + unsigned startOffset = 0; + unsigned endOffset = 0; + + Span() = default; + + Span(SourcePtr source, unsigned startOffset, unsigned endOffset) + : source(std::move(source)), startOffset(startOffset), + endOffset(endOffset) {} + + Span(Location const &loc) + : source(loc.source), startOffset(loc.offset), endOffset(loc.offset) {} + + bool ok() const { return source != nullptr; } +}; + // // error module // From 42330e5579175e6d917833e4d7192c125e6d2bf6 Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:42:10 -0400 Subject: [PATCH 4/8] Add diagnostic rendering module --- compiler/CMakeLists.txt | 1 + compiler/diagnostic.cpp | 225 ++++++++++++++++++++++++++++++++++++++++ compiler/diagnostic.hpp | 60 +++++++++++ 3 files changed, 286 insertions(+) create mode 100644 compiler/diagnostic.cpp create mode 100644 compiler/diagnostic.hpp diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index e736bd0a9..4a0d1f618 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -8,6 +8,7 @@ set(COMPILER_SOURCES codegen_op.cpp constructors.cpp desugar.cpp + diagnostic.cpp env.cpp error.cpp evaluator.cpp diff --git a/compiler/diagnostic.cpp b/compiler/diagnostic.cpp new file mode 100644 index 000000000..902028d80 --- /dev/null +++ b/compiler/diagnostic.cpp @@ -0,0 +1,225 @@ +#include "diagnostic.hpp" + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace ceramic { + +static constexpr unsigned TAB_WIDTH = 8; +static const char *SNIPPET_INDENT = " "; +static const char *LOCATION_INDENT = " "; + +static const char *ANSI_RESET = "\033[0m"; +static const char *ANSI_BOLD_RED = "\033[1;31m"; +static const char *ANSI_BOLD_YELLOW = "\033[1;33m"; +static const char *ANSI_BOLD_CYAN = "\033[1;36m"; +static const char *ANSI_BOLD_GREEN = "\033[1;32m"; + +static bool isUtf8Continuation(unsigned char c) { return (c & 0xC0) == 0x80; } + +void lineBoundsAt(SourcePtr const &source, unsigned offset, unsigned &lineStart, + unsigned &lineEnd, unsigned &lineNumber) { + lineStart = 0; + lineNumber = 1; + const char *data = source->data(); + const unsigned size = static_cast(source->size()); + const unsigned clamped = offset > size ? size : offset; + for (unsigned i = 0; i < clamped; ++i) { + if (data[i] == '\n') { + lineStart = i + 1; + ++lineNumber; + } + } + lineEnd = lineStart; + while (lineEnd < size && data[lineEnd] != '\n') + ++lineEnd; +} + +unsigned visualColumn(SourcePtr const &source, unsigned offset) { + unsigned lineStart, lineEnd, lineNumber; + lineBoundsAt(source, offset, lineStart, lineEnd, lineNumber); + const char *data = source->data(); + unsigned col = 0; + for (unsigned i = lineStart; i < offset; ++i) { + unsigned char c = static_cast(data[i]); + if (c == '\t') + col += TAB_WIDTH - (col % TAB_WIDTH); + else if (!isUtf8Continuation(c)) + ++col; + } + return col; +} + +llvm::StringRef severityWord(Severity severity) { + switch (severity) { + case Severity::Error: + return "error"; + case Severity::Warning: + return "warning"; + case Severity::Note: + return "note"; + case Severity::Help: + return "help"; + } + return "error"; +} + +static const char *severityAnsi(Severity severity) { + switch (severity) { + case Severity::Error: + return ANSI_BOLD_RED; + case Severity::Warning: + return ANSI_BOLD_YELLOW; + case Severity::Note: + return ANSI_BOLD_CYAN; + case Severity::Help: + return ANSI_BOLD_GREEN; + } + return ANSI_BOLD_RED; +} + +Renderer::Renderer() : useColor(isatty(fileno(stderr)) != 0) {} + +string Renderer::bold(llvm::StringRef s) const { + if (!useColor) + return s.str(); + string buf; + buf.reserve(s.size() + 8); + buf += "\033[1m"; + buf += s; + buf += ANSI_RESET; + return buf; +} + +string Renderer::colorSeverity(Severity severity, llvm::StringRef s) const { + if (!useColor) + return s.str(); + string buf; + buf.reserve(s.size() + 16); + buf += severityAnsi(severity); + buf += s; + buf += ANSI_RESET; + return buf; +} + +string Renderer::colorCaret(Severity severity, llvm::StringRef s) const { + return colorSeverity(severity, s); +} + +void Renderer::renderHeadline(Severity severity, llvm::StringRef msg, + llvm::raw_ostream &out) { + out << colorSeverity(severity, severityWord(severity)) << ": " << msg + << "\n"; +} + +void Renderer::renderLocationLine(Span primary, llvm::raw_ostream &out) { + if (!primary.ok()) + return; + unsigned lineStart, lineEnd, lineNumber; + lineBoundsAt(primary.source, primary.startOffset, lineStart, lineEnd, + lineNumber); + unsigned column = visualColumn(primary.source, primary.startOffset) + 1; + out << LOCATION_INDENT << "at " << primary.source->fileName << ":" + << lineNumber << ":" << column << "\n"; +} + +void Renderer::renderSnippet(Span primary, llvm::StringRef inlineLabel, + Severity severity, llvm::raw_ostream &out) { + if (!primary.ok()) + return; + + unsigned lineStart, lineEnd, lineNumber; + lineBoundsAt(primary.source, primary.startOffset, lineStart, lineEnd, + lineNumber); + const char *data = primary.source->data(); + + out << SNIPPET_INDENT; + for (unsigned i = lineStart; i < lineEnd; ++i) + out << data[i]; + out << "\n"; + + // build the caret line as plain text first, then color the marker run. + out << SNIPPET_INDENT; + unsigned spanEnd = primary.endOffset; + if (spanEnd > lineEnd) + spanEnd = lineEnd; + if (spanEnd < primary.startOffset) + spanEnd = primary.startOffset; + + unsigned col = 0; + for (unsigned i = lineStart; i < primary.startOffset; ++i) { + unsigned char c = static_cast(data[i]); + if (c == '\t') { + unsigned advance = TAB_WIDTH - (col % TAB_WIDTH); + for (unsigned k = 0; k < advance; ++k) + out << ' '; + col += advance; + } else if (!isUtf8Continuation(c)) { + out << ' '; + ++col; + } + } + + string marker; + if (primary.startOffset == spanEnd) { + marker = "^"; + } else { + bool first = true; + for (unsigned i = primary.startOffset; i < spanEnd; ++i) { + unsigned char c = static_cast(data[i]); + if (isUtf8Continuation(c)) + continue; + if (c == '\t') { + unsigned advance = TAB_WIDTH - (col % TAB_WIDTH); + for (unsigned k = 0; k < advance; ++k) { + marker += (first ? '^' : '~'); + first = false; + } + col += advance; + } else { + marker += (first ? '^' : '~'); + first = false; + ++col; + } + } + if (marker.empty()) + marker = "^"; + } + + out << colorCaret(severity, marker); + if (!inlineLabel.empty()) + out << " " << colorCaret(severity, inlineLabel); + out << "\n"; +} + +void Renderer::renderOne(Diagnostic const &diag, llvm::raw_ostream &out) { + renderHeadline(diag.severity, diag.headline, out); + renderLocationLine(diag.primary, out); + renderSnippet(diag.primary, diag.primaryLabel, diag.severity, out); + + for (auto const &lbl : diag.labels) { + // secondary labels render like a sub-note: location + snippet, with + // the label text inline on the caret line. + renderLocationLine(lbl.span, out); + renderSnippet(lbl.span, lbl.message, Severity::Note, out); + } +} + +void Renderer::render(Diagnostic const &diag, llvm::raw_ostream &out) { + renderOne(diag, out); + for (size_t i = 0; i < diag.notes.size(); ++i) + renderOne(diag.notes[i], out); + if (!diag.suggestion.empty()) { + out << colorSeverity(Severity::Help, severityWord(Severity::Help)) + << ": " << diag.suggestion << "\n"; + } + out.flush(); +} + +void displayDiagnostic(Diagnostic const &diag) { + static Renderer renderer; + renderer.render(diag, llvm::errs()); +} + +} // namespace ceramic diff --git a/compiler/diagnostic.hpp b/compiler/diagnostic.hpp new file mode 100644 index 000000000..396b0eaf2 --- /dev/null +++ b/compiler/diagnostic.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include "ceramic.hpp" + +namespace ceramic { + +enum class Severity { Error, Warning, Note, Help }; + +struct SpanLabel { + Span span; + string message; + + SpanLabel() = default; + SpanLabel(Span span, string message) + : span(span), message(std::move(message)) {} +}; + +struct Diagnostic { + Severity severity = Severity::Error; + string headline; + Span primary; + string primaryLabel; // inline label after the caret line; empty = none + vector labels; + vector notes; + string suggestion; + + Diagnostic() = default; + Diagnostic(Severity severity, string headline, Span primary) + : severity(severity), headline(std::move(headline)), primary(primary) {} +}; + +class Renderer { + public: + Renderer(); + + void render(Diagnostic const &diag, llvm::raw_ostream &out); + + private: + bool useColor; + + void renderOne(Diagnostic const &diag, llvm::raw_ostream &out); + void renderHeadline(Severity severity, llvm::StringRef msg, + llvm::raw_ostream &out); + void renderLocationLine(Span primary, llvm::raw_ostream &out); + void renderSnippet(Span primary, llvm::StringRef inlineLabel, + Severity severity, llvm::raw_ostream &out); + string bold(llvm::StringRef s) const; + string colorSeverity(Severity severity, llvm::StringRef s) const; + string colorCaret(Severity severity, llvm::StringRef s) const; +}; + +unsigned visualColumn(SourcePtr const &source, unsigned offset); +void lineBoundsAt(SourcePtr const &source, unsigned offset, unsigned &lineStart, + unsigned &lineEnd, unsigned &lineNumber); + +void displayDiagnostic(Diagnostic const &diag); + +llvm::StringRef severityWord(Severity severity); + +} // namespace ceramic From f85ce9d50f2694e4edabbb45b338b135c345f5ac Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:43:48 -0400 Subject: [PATCH 5/8] Wire displayError through Diagnostic --- compiler/error.cpp | 103 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/compiler/error.cpp b/compiler/error.cpp index e148e4d9d..983b1beb9 100644 --- a/compiler/error.cpp +++ b/compiler/error.cpp @@ -1,6 +1,7 @@ #include "error.hpp" #include "ceramic.hpp" #include "codegen.hpp" +#include "diagnostic.hpp" #include "evaluator.hpp" #include "invoketables.hpp" #include "matchinvoke.hpp" @@ -75,6 +76,7 @@ CompileContextPusher::CompileContextPusher( // static vector errorLocations; +static vector errorSpans; void pushLocation(Location const &location) { errorLocations.push_back(location); @@ -93,6 +95,28 @@ Location topLocation() { return {}; } +static Span topSpan() { + for (auto i = errorSpans.rbegin(); i != errorSpans.rend(); ++i) { + if (i->ok()) + return *i; + } + return {}; +} + +namespace { +struct SpanHint { + bool active; + SpanHint(Span const &s) : active(s.ok()) { + if (active) + errorSpans.push_back(s); + } + ~SpanHint() { + if (active) + errorSpans.pop_back(); + } +}; +} // namespace + // // DebugPrinter // @@ -262,25 +286,76 @@ static void displayDebugStack() { } } -void displayError(llvm::Twine const &msg, llvm::StringRef kind) { - string msgString = msg.str(); - if (msgString.empty() || msgString[msgString.length() - 1] != '\n') - msgString += '\n'; +static string stripTrailingNewline(llvm::Twine const &msg) { + string s = msg.str(); + if (!s.empty() && s.back() == '\n') + s.pop_back(); + return s; +} - Location location = topLocation(); - if (location.ok()) { - unsigned line, column; - displayLocation(location, line, column); - llvm::errs() << location.source->fileName << '(' << line + 1 << ',' - << column << "): " << kind << ": " << msgString; - llvm::errs().flush(); - displayCompileContext(); - displayDebugStack(); +static Severity severityFromKind(llvm::StringRef kind) { + if (kind == "warning") + return Severity::Warning; + if (kind == "note") + return Severity::Note; + if (kind == "help") + return Severity::Help; + return Severity::Error; +} + +static string compileFrameHeadline(CompileContextEntry const &frame) { + string buf; + llvm::raw_string_ostream sout(buf); + sout << "while compiling "; + ObjectPtr obj = frame.callable; + if (obj->objKind == GLOBAL_VARIABLE) { + sout << "global "; + printName(sout, obj); + if (!frame.params.empty()) { + sout << "["; + printNameList(sout, llvm::ArrayRef(frame.params)); + sout << "]"; + } } else { - llvm::errs() << kind << ": " << msgString; + printName(sout, obj); + if (frame.hasParams) { + sout << "("; + printNameList(sout, llvm::ArrayRef(frame.params), + llvm::ArrayRef(frame.dispatchIndices)); + sout << ")"; + } + } + sout.flush(); + return buf; +} + +static void appendContextNotes(Diagnostic &diag, Location primaryLocation) { + for (size_t i = 0; i < contextStack.size(); ++i) { + CompileContextEntry const &frame = contextStack[i]; + if (!frame.location.ok()) + continue; + if (frame.overload.ptr() != nullptr && + frame.overload->isDiagnosticTransparent) + continue; + if (primaryLocation.ok() && + frame.location.source == primaryLocation.source && + frame.location.offset == primaryLocation.offset) + continue; + diag.notes.emplace_back(Severity::Note, compileFrameHeadline(frame), + Span(frame.location)); } } +void displayError(llvm::Twine const &msg, llvm::StringRef kind) { + Location location = topLocation(); + Span span = topSpan(); + if (!span.ok()) + span = Span(location); + Diagnostic diag(severityFromKind(kind), stripTrailingNewline(msg), span); + appendContextNotes(diag, location); + displayDiagnostic(diag); +} + void warning(llvm::Twine const &msg) { displayError(msg, "warning"); } void note(llvm::Twine const &msg) { displayError(msg, "note"); } From c5defc4c9091bcb8fa54d32137ddde6149707e7c Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:44:08 -0400 Subject: [PATCH 6/8] Add error() overloads that accept Expr context --- compiler/error.cpp | 21 +++++++++++++++++++++ compiler/error.hpp | 2 ++ 2 files changed, 23 insertions(+) diff --git a/compiler/error.cpp b/compiler/error.cpp index 983b1beb9..80a1b6584 100644 --- a/compiler/error.cpp +++ b/compiler/error.cpp @@ -371,6 +371,27 @@ void error(Location const &location, llvm::Twine const &msg) { error(msg); } +static Span exprSpan(Expr const *e) { + if (e == nullptr) + return {}; + if (e->startLocation.ok() && e->endLocation.ok() && + e->startLocation.source == e->endLocation.source) + return Span(e->startLocation.source, e->startLocation.offset, + e->endLocation.offset); + return {}; +} + +void error(Expr const *context, llvm::Twine const &msg) { + SpanHint hint(exprSpan(context)); + if (context != nullptr && context->location.ok()) + pushLocation(context->location); + error(msg); +} + +void error(Pointer context, llvm::Twine const &msg) { + error(context.ptr(), msg); +} + void fmtError(const char *fmt, ...) { va_list ap; char s[256]; diff --git a/compiler/error.hpp b/compiler/error.hpp index 2d344f813..87f1288b3 100644 --- a/compiler/error.hpp +++ b/compiler/error.hpp @@ -14,6 +14,8 @@ namespace ceramic { void error(llvm::Twine const &msg) CERAMIC_NORETURN; void error(Location const &location, llvm::Twine const &msg) CERAMIC_NORETURN; +void error(Expr const *context, llvm::Twine const &msg) CERAMIC_NORETURN; +void error(Pointer context, llvm::Twine const &msg) CERAMIC_NORETURN; void warning(llvm::Twine const &msg); From 263c7f7f079dc700120a1f12eaf34c8f2063730d Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:44:35 -0400 Subject: [PATCH 7/8] Tighten error message wording --- compiler/error.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/error.cpp b/compiler/error.cpp index 80a1b6584..a5e7d0889 100644 --- a/compiler/error.cpp +++ b/compiler/error.cpp @@ -414,7 +414,7 @@ void arityError(size_t expected, size_t received) { string buf; llvm::raw_string_ostream sout(buf); sout << "expected " << expected << " " << valuesStr(expected); - sout << ", but received " << received << " " << valuesStr(received); + sout << ", found " << received; error(sout.str()); } @@ -423,7 +423,7 @@ void arityError2(size_t minExpected, size_t received) { llvm::raw_string_ostream sout(buf); sout << "expected at least " << minExpected << " " << valuesStr(minExpected); - sout << ", but received " << received << " " << valuesStr(received); + sout << ", found " << received; error(sout.str()); } @@ -455,8 +455,7 @@ void arityMismatchError(size_t leftArity, size_t rightArity, bool hasVarArg) { << valuesStr(leftArity); else sout << "left side has " << leftArity << " " << valuesStr(leftArity); - sout << ", but right side has " << rightArity << " " - << valuesStr(rightArity); + sout << ", right side has " << rightArity << " " << valuesStr(rightArity); error(sout.str()); } @@ -464,8 +463,7 @@ static string typeErrorMessage(llvm::StringRef expected, const TypePtr &receivedType) { string buf; llvm::raw_string_ostream sout(buf); - sout << "expected " << expected << ", " - << "but received " << receivedType->toString() << " type"; + sout << "expected " << expected << ", found " << receivedType->toString(); return sout.str(); } @@ -499,8 +497,8 @@ void indexRangeError(const llvm::StringRef kind, const size_t value, const size_t maxValue) { string buf; llvm::raw_string_ostream sout(buf); - sout << kind << " " << value << " is out of range. "; - sout << "it should be less than " << maxValue; + sout << kind << " " << value << " out of range, must be less than " + << maxValue; error(sout.str()); } @@ -508,8 +506,8 @@ void argumentIndexRangeError(const unsigned index, const llvm::StringRef kind, const size_t value, const size_t maxValue) { string buf; llvm::raw_string_ostream sout(buf); - sout << kind << " " << value << " is out of range. "; - sout << "it should be less than " << maxValue; + sout << kind << " " << value << " out of range, must be less than " + << maxValue; argumentError(index, sout.str()); } From 09105868aaaf276848f47f4265e5e4521438b758 Mon Sep 17 00:00:00 2001 From: Ramon Asuncion Date: Thu, 28 May 2026 07:45:48 -0400 Subject: [PATCH 8/8] Fix matchFailureError blame location and codegen statement location --- compiler/codegen.cpp | 1 + compiler/error.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/codegen.cpp b/compiler/codegen.cpp index cd0d1a918..9541e21d3 100644 --- a/compiler/codegen.cpp +++ b/compiler/codegen.cpp @@ -3237,6 +3237,7 @@ static void codegenEndScope(size_t marker, bool terminated, } bool codegenStatement(StatementPtr stmt, EnvPtr env, CodegenContext *ctx) { + LocationContext loc(stmt->location); if (llvmDIBuilder != nullptr) DebugLocationContext loc(stmt->location, ctx); diff --git a/compiler/error.cpp b/compiler/error.cpp index a5e7d0889..421666f65 100644 --- a/compiler/error.cpp +++ b/compiler/error.cpp @@ -646,6 +646,7 @@ void matchFailureError(MatchFailureError const &err) { matchFailureMessage(err, buf); // Pick the deepest blameable frame. + Location blame; for (auto it = contextStack.rbegin(); it != contextStack.rend(); ++it) { if (!it->location.ok() || !it->location.source) continue; @@ -653,11 +654,16 @@ void matchFailureError(MatchFailureError const &err) { continue; if (it->overload->isDiagnosticTransparent) continue; - pushLocation(it->location); + blame = it->location; break; } - error(buf); + if (blame.ok()) { + LocationContext lc(blame); + error(buf); + } else { + error(buf); + } } void matchFailureLog(MatchFailureError const &err) {